Hind Forsum
Hind Forsum

Reputation: 10507

Linux set priority function is not taking effect in my test

A very simple test code piece:

#include<unistd.h>
#include<sys/resource.h>
#include<sys/syscall.h>
#include<sys/types.h>
#include<stdio.h>
int main()
{
    int i=0;
    pid_t pid=getpid();
    pid_t tid=syscall(SYS_gettid);
    printf("%d,%d\n",pid,tid);
    setpriority(PRIO_PROCESS,0,-2);
    while(true){
        ++i;
    }
    return 0;
}

setpriority is using '0' as second parameter,indicating current process, as per the man page:

   The  value  which  is one of PRIO_PROCESS, PRIO_PGRP, or PRIO_USER, and
   who  is  interpreted  relative  to  which  (a  process  identifier  for
   PRIO_PROCESS, process group identifier for PRIO_PGRP, and a user ID for
   PRIO_USER).  A zero value for who denotes  (respectively)  the  calling
   process,  the process group of the calling process, or the real user ID
   of the calling process.  Prio is a value in the range -20  to  19  (but
   see  the  Notes  below).   The  default priority is 0; lower priorities
   cause more favorable scheduling.

Compile and run it, inside "top" command, the "PR" value of "a.out" is still 20, not being set with "20-2" as I expected.

Is my "setpriority" taking effect?

Upvotes: 0

Views: 1887

Answers (1)

JimD.
JimD.

Reputation: 2403

My first advice is to check the return value of setpriority() to see if it returned an error. My expectation is that it is indicating an error by returning -1 and that checking errno would reveal that EACCES was the error indicating that the caller did not have the required privilege.

There are three ways to fix this:

1) Make the executable SUID root or run it with sudo (not secure).

2) Grant the executable the capability CAP_SYS_NICE (e.g. sudo setcap cap_sys_nice=ep <executable>).

3) Adjust the hard and soft limits for the shell (ulimit) or user (depending on distro /etc/security/limits.conf). Or just adjust the hard limit and let the program adjust the soft limit.

Note that SUID and file capabilities are not effective on partitions mounted with nosuid (encrypted home directories often are).

EDIT: @TrentP has pointed out that you can drop privileges. Below is some code that is a fair example of how to do this, although it works for real-time priority:

/*
 * Set_policy_priority
 *
 * This is used to set the priority and policy for the real-time
 * scheduler.  This normally requires some form of privilege, as the
 * default hard ulimit of 0 will prevent an unprivileged program from
 * doing so.
 *
 * The most secure thing to do is grant the executable the potential
 * to enable CAP_SYS_RESOURCE (with sudo setcap cap_sys_resource=p
 * <executable>).  If it is needed, this routine will enable the
 * capability, raise the hard limit, and then irrevocably drop the
 * privilege.
 *
 */

int set_policy_priority(int policy, int priority, int nofiles) {
  const cap_value_t cap_vector[1] = { CAP_SYS_RESOURCE };
  cap_t privilege_dropped = cap_init();
  cap_t privilege_off = cap_dup(privilege_dropped);
  cap_set_flag(privilege_off, CAP_PERMITTED, 1, cap_vector, CAP_SET);
  cap_t privilege_on = cap_dup(privilege_off);
  cap_set_flag(privilege_on, CAP_EFFECTIVE, 1, cap_vector, CAP_SET);

  struct sched_param param;
  struct rlimit rl;
  int e, min, max;

  // See if priority we want is in the range offered by SCHED_FIFO

  min = sched_get_priority_min(policy);
  max = sched_get_priority_max(policy);
  if (verbose) {
fprintf(stderr, "For policy SCHED_FIFO min priority is %d, max is %d.\n", min, max);
  }

  if ((min>priority)||(max<priority)) {
fprintf(stderr, "Desired priority of %d is out of range.\n", priority);
return 1;
  }

  // See if the RTPRIO limits allows the priority we want

  if (getrlimit(RLIMIT_RTPRIO, &rl) != 0) {
e = errno;
fprintf(stderr, "Failed to getrlimit(): %s.\n", strerror(e));
return 1;
  }

  if (verbose) {
fprintf(stderr, "RTPRIO soft limit is %d, hard is %d.\n", 
    (int) rl.rlim_cur, (int) rl.rlim_max);
  }

  // Adjust hard limit if necessary

  if (rl.rlim_max < priority) {
if (cap_set_proc(privilege_on) != 0) {
  fprintf(stderr, "Need to raise RTPRIO hard limit, but can't enable CAP_SYS_RESOURCE.\n");
  return 1;
}
rl.rlim_max = priority;
if (setrlimit(RLIMIT_RTPRIO, &rl) != 0) {
  e = errno;
  fprintf(stderr, "Failed to raise hard limit for RTPRIO to %d: %s.\n", 
     (int) rl.rlim_max, strerror(e));
  return 1;
}
if (cap_set_proc(privilege_off)) {
  fprintf(stderr, "Failed to turn off privileges.\n");
  return 1;
}
if (verbose) {
  printf("Raised hard limit for RTPRIO to %d.\n", (int) rl.rlim_max);
}
  }

  // Adjust soft limit if necessary

  if (rl.rlim_cur < priority) {
rl.rlim_cur = priority;
if (setrlimit(RLIMIT_RTPRIO, &rl) != 0) {
  e = errno;
  fprintf(stderr, "Failed to raise soft limit for RTPRIO to %d: %s.\n", 
     (int) rl.rlim_cur, strerror(e));
  return 1;
}
if (verbose) {
  printf("Raised soft limit for RTPRIO to %d.\n", (int) rl.rlim_cur);
}
  }

  // Set desired priority with class SCHED_FIFO

  param.sched_priority = priority;
  if (sched_setscheduler(0, policy, &param) != 0) {
e = errno;
fprintf(stderr, "Setting policy failed: %s.\n", strerror(e));
return 1;
  } else if (verbose) {
printf("Set policy SCHED_FIFO, priority %d.\n", param.sched_priority);
  }

  // See if the NOFILE limits allows the number of fds we want

  if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
e = errno;
fprintf(stderr, "Failed to getrlimit(): %s.\n", strerror(e));
return 1;
  }

  if (verbose) {
fprintf(stderr, "NOFILE soft limit is %d, hard is %d.\n", 
    (int) rl.rlim_cur, (int) rl.rlim_max);
  }

  // Adjust hard limit if necessary

  if (rl.rlim_max < nofiles) {
if (cap_set_proc(privilege_on) != 0) {
  fprintf(stderr, "Need to raise NOFILE hard limit, but can't enable CAP_SYS_RESOURCE.\n");
  return 1;
}
rl.rlim_max = nofiles;
if (setrlimit(RLIMIT_NOFILE, &rl) != 0) {
  e = errno;
  fprintf(stderr, "Failed to raise hard limit for NOFILE to %d: %s.\n", 
     (int) rl.rlim_max, strerror(e));
  return 1;
}
if (cap_set_proc(privilege_off)) {
  fprintf(stderr, "Failed to turn off privileges.\n");
  return 1;
}
if (verbose) {
  printf("Raised hard limit for NOFILE to %d.\n", (int) rl.rlim_max);
}
  }

  // Adjust soft limit if necessary

  if (rl.rlim_cur < nofiles) {
rl.rlim_cur = nofiles;
if (setrlimit(RLIMIT_NOFILE, &rl) != 0) {
  e = errno;
  fprintf(stderr, "Failed to raise soft limit for NOFILE to %d: %s.\n", 
     (int) rl.rlim_cur, strerror(e));
  return 1;
}
if (verbose) {
  printf("Raised soft limit for NOFILE to %d.\n", (int) rl.rlim_cur);
}
  }

  if (cap_set_proc(privilege_dropped)) {
fprintf(stderr, "Failed to turn irrevocably drop privileges.\n");
return 1;
  }

  return 0;
}

Upvotes: 4

Related Questions