From c4717c8b0add43a67a8ff052b458306230259e11 Mon Sep 17 00:00:00 2001 From: Bo Borgerson Date: Fri, 25 Apr 2008 18:58:08 -0400 Subject: [PATCH] Add new program: renice * doc/coreutils.texi: Explain new program. * man/renice.x: Manpage template for new program. * po/POTFILES.in: List new program. * src/Makefile.am: List new program. * src/renice.c: New program. * tests/Makefile.am: List test for new program. * tests/misc/renice: Tests for new program. * AUTHORS: Register as author. * README: List new program. Signed-off-by: Bo Borgerson --- AUTHORS | 1 + README | 2 +- doc/coreutils.texi | 72 +++++++++- man/renice.x | 6 + po/POTFILES.in | 1 + src/Makefile.am | 2 +- src/renice.c | 412 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 1 + tests/misc/renice | 93 ++++++++++++ 9 files changed, 587 insertions(+), 3 deletions(-) create mode 100644 man/renice.x create mode 100644 src/renice.c create mode 100755 tests/misc/renice diff --git a/AUTHORS b/AUTHORS index 807857f..1840f0c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -61,6 +61,7 @@ printf: David MacKenzie ptx: F. Pinard pwd: Jim Meyering readlink: Dmitry V. Levin +renice: Bo Borgerson rm: Paul Rubin, David MacKenzie, Richard Stallman, Jim Meyering rmdir: David MacKenzie runcon: Russell Coker diff --git a/README b/README index 7a608f4..3bfd4e9 100644 --- a/README +++ b/README @@ -11,7 +11,7 @@ The programs that can be built with this package are: csplit cut date dd df dir dircolors dirname du echo env expand expr factor false fmt fold groups head hostid hostname id install join kill link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup - od paste pathchk pinky pr printenv printf ptx pwd readlink rm rmdir + od paste pathchk pinky pr printenv printf ptx pwd readlink renice rm rmdir runcon seq sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf sleep sort split stat stty su sum sync tac tail tee test touch tr true tsort tty uname unexpand uniq unlink uptime users vdir wc who whoami yes diff --git a/doc/coreutils.texi b/doc/coreutils.texi index f42e736..17f993f 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -92,6 +92,7 @@ * ptx: (coreutils)ptx invocation. Produce permuted indexes. * pwd: (coreutils)pwd invocation. Print working directory. * readlink: (coreutils)readlink invocation. Print referent of a symlink. +* renice: (coreutils)renice invocation. Modify niceness of processes. * rm: (coreutils)rm invocation. Remove files. * rmdir: (coreutils)rmdir invocation. Remove empty directories. * seq: (coreutils)seq invocation. Print numeric sequences @@ -191,7 +192,7 @@ Free Documentation License''. * User information:: id logname whoami groups users who * System context:: date uname hostname hostid * Modified command invocation:: chroot env nice nohup su -* Process control:: kill +* Process control:: kill renice * Delaying:: sleep * Numeric operations:: factor seq * File permissions:: Access modes. @@ -425,6 +426,7 @@ Modified command invocation Process control * kill invocation:: Sending a signal to processes. +* renice invocation:: Modify the niceness of processes. Delaying @@ -13872,6 +13874,7 @@ might find this idea strange at first. @menu * kill invocation:: Sending a signal to processes. +* renice invocation:: Modify the niceness of processes. @end menu @@ -14024,6 +14027,72 @@ File size limit exceeded. also support at least eight real-time signals called @samp{RTMIN}, @samp{RTMIN+1}, @dots{}, @samp{RTMAX-1}, @samp{RTMAX}. address@hidden renice invocation address@hidden @command{renice}: Modify the niceness of processes + address@hidden renice address@hidden modify the niceness of processes + +The @command{renice} command adjusts the niceness of running processes, +affecting process scheduling. Nicenesses range from -20 (most favorable +scheduling) to 19 (least favorable). + address@hidden +renice -n @var{adjustment} [-p | -g | -u] @address@hidden +renice @var{priority} [-p | -g | -u ] @address@hidden address@hidden example + +The first form of the @command{renice} adjusts the niceness of all +processes referenced by process ids @option{-p}, process group ids address@hidden, usernames or user ids @option{-u} by a certain amount address@hidden The default adjustment is an increase of 10. + address@hidden @samp address@hidden -n @var{n} address@hidden address@hidden address@hidden -n address@hidden --adjustment address@hidden set the niceness adjustment +Add integer @var{n} to the niceness (default 10). address@hidden table + +Alternatively an absolute desired niceness can be provided as the first +operand. This value is not preceded by an option switch. + +The following options affect how subsequent operands are interpreted. +They behave as multi-argument options. If none of these options is +in effect then arguments are interpreted as @var{process id}s. + address@hidden @samp address@hidden -p address@hidden --process address@hidden -p address@hidden --process address@hidden interpret operands as process ids +Interperet subsequent operads as @var{process id}s. + address@hidden -g address@hidden --group address@hidden -g address@hidden --group address@hidden interpret operands as process group ids +Interperet subsequent operads as @var{process group id}s. + address@hidden -u address@hidden --user address@hidden -u address@hidden --user address@hidden interpret operands as usernames or user ids +Interperet subsequent operads as @var{username}s or @var{user id}s. + address@hidden table + +Examples of usage + address@hidden +renice 10 987 32 # Set niceness to 10 for processes 987 and 32 +renice -n 5 -g 1031 # Add 5 to the nicenesses of all processes in group 1031 address@hidden example @node Delaying @chapter Delaying @@ -14033,6 +14102,7 @@ also support at least eight real-time signals called @samp{RTMIN}, @c Perhaps @command{wait} or other commands should be described here also? + @menu * sleep invocation:: Delay for a specified time. @end menu diff --git a/man/renice.x b/man/renice.x new file mode 100644 index 0000000..2545cb5 --- /dev/null +++ b/man/renice.x @@ -0,0 +1,6 @@ +[NAME] +renice \- adjust the niceness of running processes +[DESCRIPTION] +.\" Add any additional description here +[SEE ALSO] +nice(1) diff --git a/po/POTFILES.in b/po/POTFILES.in index e975109..fc55ab3 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -93,6 +93,7 @@ src/ptx.c src/pwd.c src/readlink.c src/remove.c +src/renice.c src/rm.c src/rmdir.c src/runcon.c diff --git a/src/Makefile.am b/src/Makefile.am index 668e178..cca5826 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,7 +34,7 @@ EXTRA_PROGRAMS = \ mkfifo mknod mktemp \ mv nohup readlink rm rmdir shred stat sync touch unlink \ cat cksum comm csplit cut expand fmt fold head join groups md5sum \ - nl od paste pr ptx sha1sum sha224sum sha256sum sha384sum sha512sum \ + nl od paste pr ptx renice sha1sum sha224sum sha256sum sha384sum sha512sum \ shuf sort split sum tac tail tr tsort unexpand uniq wc \ basename date dirname echo env expr factor false \ id kill logname pathchk printenv printf pwd \ diff --git a/src/renice.c b/src/renice.c new file mode 100644 index 0000000..2a18c1e --- /dev/null +++ b/src/renice.c @@ -0,0 +1,412 @@ +/* renice -- adjust the niceness of running processes + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Bo Borgerson */ + +/* Substantially based off of gnu `nice' by David MacKenzie. */ + +#include +#include +#include +#include +#include + +#include "quote.h" + +#include "system.h" + +#if ! HAVE_NICE +/* Include this after "system.h" so we're sure to have definitions + (from time.h or sys/time.h) required for e.g. the ru_utime member. */ +# include +#endif + +#include "error.h" +#include "long-options.h" +#include "quote.h" +#include "xstrtol.h" + +/* The official name of this program (e.g., no `g' prefix). */ +#define PROGRAM_NAME "renice" + +#define AUTHORS "Bo Borgerson" + +#if HAVE_NICE +# define GET_NICENESS() nice (0) +#else +# define GET_NICENESS() getpriority (PRIO_PROCESS, 0) +#endif + +#ifndef NZERO +# define NZERO 20 +#endif + +/* This is required for Darwin Kernel Version 7.7.0. */ +#if NZERO == 0 +# undef NZERO +# define NZERO 20 +#endif + +/* The name this program was run with. */ +char *program_name; + +/* This will be added to the niceness of each process. This can be modified + on the command line with the `-n' or `--adjustment'. */ +static int adjustment = 10; + +/* Based on input we might set the priority of each process to a specific + value or we might make an adjustment relative to the current value. + Default is a relative adjustment by 10. */ +enum { ADJUSTMENT_RELATIVE, ADJUSTMENT_ABSOLUTE }; +static int adjustment_type = ADJUSTMENT_RELATIVE; + +/* This will be set if we found something that looks like a valid ID. + If we don't find anything then we'll just print a usage statement + and exit. */ +static bool have_ids = false; + +/* This is used to record errors that are encountered after initial input + validation. */ +static bool had_error = false; + +/* This contains all the information for IDs of a given type (processes, + users or groups). */ +struct id_list + { + size_t count; /* Number of elements present. */ + size_t alloc; /* Number of elements for which we've made space. */ + int which; /* What the elements are. */ + char *type; /* A string representing what the elements are. */ + int *who; /* The IDs for each element. */ + char **name; /* The input string that referenced each element. */ + int *cur; /* The current nicenesses for each element. */ + }; + +static struct option const longopts[] = +{ + {"adjustment", required_argument, NULL, 'n'}, + {"group", no_argument, NULL, 'g'}, + {"user", no_argument, NULL, 'u'}, + {"process", no_argument, NULL, 'p'}, + {NULL, 0, NULL, 0} +}; + +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("Usage: %s [PRIORITY] [OPTION]... ID ...\n"), program_name); + printf (_("\ +Adjust the niceness of running processes, affecting process scheduling.\n\ +Nicenesses range from %d (most favorable scheduling) to %d (least \ +favorable).\n\ +\n\ +"), - NZERO, NZERO - 1); + fputs (_("\ +If PRIORITY is given it is taken as absolute. If no PRIORITY is given\n\ +then the ADJUSTMENT to niceness is relative, defaulting to an increase of\n\ +10.\n\ +\n\ +"), stdout); + fputs (_("\ + -n, --adjustment=N add integer N to the niceness (default 10)\n\ +\n\ +The following options affect how subsequent operands are interpreted.\n\ +They behave as multi-argument options. If none of these options is\n\ +in effect then arguments are interpreted as PIDs.\n\ +\n\ +"), stdout); + fputs (_("\ + -p, --process subsequent operands are process ids (default)\n\ + -g, --group subsequent operands are process group ids\n\ + -u, --user subsequent operands are usernames or user ids\n\ +"), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME); + emit_bug_reporting_address (); + } + exit (status); +} + +/* Make sure the requested adjustement is in a reasonable range. */ +static void +set_adjustment (char *adjustment_given) +{ + /* If the requested adjustment is outside the valid range, + silently bring it to just within range; this mimics what + "setpriority" and "nice" do. */ + enum { MIN_ADJUSTMENT = 1 - 2 * NZERO, + MAX_ADJUSTMENT = 2 * NZERO - 1 }; + long int tmp; + + if (LONGINT_OVERFLOW < xstrtol (adjustment_given, NULL, 10, &tmp, "")) + error (EXIT_FAILURE, 0, _("invalid adjustment %s"), + quote (adjustment_given)); + + adjustment = MAX (MIN_ADJUSTMENT, MIN (tmp, MAX_ADJUSTMENT)); +} + +/* Set some list properties and zero everything else out. */ +static void +init_id_list (struct id_list *ids, int which, char *type) +{ + ids->which = which; + ids->type = type; + ids->count = 0; + ids->alloc = 0; + ids->who = NULL; + ids->name = NULL; + ids->cur = NULL; +} + +/* Cleanup just for completeness. */ +static void +free_id_list (struct id_list *ids) +{ + if (ids->alloc) + { + free (ids->who); + free (ids->name); + free (ids->cur); + }; +} + +#define ADD_ID_ERROR_INVALID \ + error (EXIT_FAILURE, 0, _("invalid %s: %s"), ids->type, quote (id_str)); + +/* Add a process, process group or user to the appropriate list. */ +static void +add_id (struct id_list *ids, char *id_str) +{ + uintmax_t candidate; + bool got_it = false; + struct passwd *pwd; + + if (ids->count == ids->alloc) + { + ids->who = X2NREALLOC (ids->who, &ids->alloc); + ids->name = xnrealloc (ids->name, ids->alloc, sizeof *ids->name); + ids->cur = xnrealloc (ids->cur, ids->alloc, sizeof *ids->cur); + } + + if (ids->which == PRIO_USER && ((pwd = getpwnam (id_str)) != NULL)) + ids->who[ids->count] = pwd->pw_uid; + else + { + if (xstrtoumax (id_str, NULL, 0, &candidate, NULL) != LONGINT_OK) + ADD_ID_ERROR_INVALID; + + ids->who[ids->count] = candidate; + + if (ids->who[ids->count] != candidate) + ADD_ID_ERROR_INVALID; + + /* Fall back to lookup by ID for users, too. */ + if (ids->which == PRIO_USER) + { + if((pwd = getpwuid (ids->who[ids->count])) == NULL) + ADD_ID_ERROR_INVALID; + + ids->who[ids->count] = pwd->pw_uid; + } + } + + ids->name[ids->count] = id_str; + + ids->count++; + + have_ids = true; + +} + +/* Get current niceness for all processes, groups or users referenced by + IDS. */ +static void +get_nicenesses (struct id_list *ids) +{ + int which = ids->which; + size_t i; + + for (i = 0; i < ids->count; i++) + { + int who = ids->who[i]; + char *who_str = ids->name[i]; + int current_niceness; + + errno = 0; + + ids->cur[i] = getpriority (which, who); + + /* Failure to get priority is fatal. */ + if (ids->cur[i] == -1 && errno != 0) + error (EXIT_FAILURE, errno, _("cannot get niceness for %s %s"), + ids->type, quote (who_str)); + + } +} + +/* Set niceness for all processes, groups or users referenced by IDS. */ +static void +set_nicenesses (struct id_list *ids) +{ + int which = ids->which; + size_t i; + + for (i = 0; i < ids->count; i++) + { + int who = ids->who[i]; + char *who_str = ids->name[i]; + int current_niceness = ids->cur[i]; + int new_niceness = (adjustment_type == ADJUSTMENT_RELATIVE) + ? current_niceness + adjustment + : adjustment; + + errno = 0; + + if ((setpriority (which, who, new_niceness)) != 0) + error (0, errno, _("cannot set niceness for %s %s"), ids->type, + quote (who_str)); + + if (errno) + had_error = true; + + } +} + +int +main (int argc, char **argv) +{ + int c, i; + enum { IDS_PROC, IDS_USER, IDS_GROUP }; + int cur_id_type = IDS_PROC; + struct id_list ids[3]; + int cur_argc; + char **fake_argv = xnmalloc (argc + 1, sizeof *fake_argv); + + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + initialize_exit_failure (EXIT_FAILURE); + atexit (close_stdout); + + init_id_list (&ids[IDS_PROC], PRIO_PROCESS, "pid"); + init_id_list (&ids[IDS_GROUP], PRIO_PGRP, "gid"); + init_id_list (&ids[IDS_USER], PRIO_USER, "user"); + + /* This unusual option parsing loop is used to support stateful options + which affect subsequent arguments until a new option is encountered. */ + for (i = 1; i < argc; /* Managed within the loop body. */) + { + + if (i == 1 && (isdigit (argv[i][0]) + || ((argv[i][0] == '-' || argv[i][0] == '+') + && isdigit (argv[i][1])))) + { + adjustment_type = ADJUSTMENT_ABSOLUTE; + set_adjustment ((argv[i][0] == '+') + ?argv[i] + 1 + :argv[i]); + i++; + } + else + { + int c, j; + int fake_argc = argc - (i - 1); + + fake_argv[0] = argv[0]; + for (j = i; j < argc; j++) + fake_argv[j - i + 1] = argv[j]; + fake_argv[fake_argc] = NULL; + + parse_long_options (fake_argc, fake_argv, PROGRAM_NAME, + PACKAGE_NAME, VERSION, usage, AUTHORS, + (char const *) NULL); + + optind = 0; + while ((c = getopt_long (fake_argc, fake_argv, "+g:n:p:u:", + longopts, NULL)) != -1) + switch (c) + { + + case 'n': + + /* We may have interpreted an argument before this option + incorrectly. Try to be GNUish here and don't require + options to come before arguments. */ + if (adjustment_type == ADJUSTMENT_ABSOLUTE) + add_id (&ids[IDS_PROC], argv[1]); + + adjustment_type = ADJUSTMENT_RELATIVE; + set_adjustment (optarg); + break; + + case 'g': + cur_id_type = IDS_GROUP; + break; + case 'p': + cur_id_type = IDS_PROC; + break; + case 'u': + cur_id_type = IDS_USER; + break; + + case_GETOPT_HELP_CHAR; + + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + + default: + usage (EXIT_FAILURE); + } + + /* Any remaining operands are assumed to be whatever the last + type was until a new type is set, or process IDs if no types + were specified. */ + for (i += optind - 1; i < argc && argv[i][0] != '-'; i++) + add_id (&ids[cur_id_type], argv[i]); + + } + } + free (fake_argv); + + /* This is set in add_id. */ + if (! have_ids) + usage (EXIT_FAILURE); + + /* Get all first, so we're sure to adjust relative to what the niceness + was prior to invocation in case we hit the same process twice. */ + for (i = 0; i < 3; i++) + get_nicenesses (&ids[i]); + + /* Once we get this far errors aren't immediately fatal, since all input + parameters have proven to map to something that at least HAS a + priority. We still keep track of whether an error was encountered, + though, and set our exit code accordingly, below. */ + for (i = 0; i < 3; i++) + { + set_nicenesses (&ids[i]); + free_id_list (&ids[i]); + } + + exit (had_error ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 7dfafac..e9ffdb3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -179,6 +179,7 @@ TESTS = \ misc/printf-surprise \ misc/pwd-long \ misc/readlink-fp-loop \ + misc/renice \ misc/runcon-no-reorder \ misc/sha1sum \ misc/sha1sum-vec \ diff --git a/tests/misc/renice b/tests/misc/renice new file mode 100755 index 0000000..9dbe499 --- /dev/null +++ b/tests/misc/renice @@ -0,0 +1,93 @@ +#!/bin/sh +# Test renice + +# Copyright (C) 2008 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +: ${srcdir=.} +. $top_srcdir/tests/require-perl + +me=`echo $0|sed 's,.*/,,'` +exec $PERL -w -I$top_srcdir/tests -MCoreutils -M"CuTmpdir qw($me)" -- - <<\EOF +#/ +require 5.003; +use strict; + +(my $program_name = $0) =~ s|.*/||; + +my $prog = 'renice'; + +my $pid = fork(); + +defined $pid or die "Failed to fork: $!"; + +if (!$pid) + { + sleep 1 while 1; + } + +# Turn off localization of executable's ouput. address@hidden(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my @Tests = + ( + # invalid naked invocation + ['usage', {EXIT=>1}, + {ERR => "Try `$prog --help' for more information.\n"}], + + # invalid invocation with no process identifiers + ['usage-2', '10', {EXIT=>1}, + {ERR => "Try `$prog --help' for more information.\n"}], + + # invalid invocation with negative PID + ['pid', '10', '-1', {EXIT=>1}, + {ERR => "$prog: invalid option -- 1\n". + "Try `$prog --help' for more information.\n"}], + + # invalid invocation with pid overflow + ['pid-2', '10', '18446744073709551616', {EXIT=>1}, + {ERR => "$prog: invalid pid: `18446744073709551616'\n" }], + + # invalid invocation with nonexistent pid + # FIXME? Anyone have pid_max > 2^30? + ['pid-3', '10', '1073741824', {EXIT=>1}, + {ERR_SUBST => 's/:[^:]+$//'}, # strerror may be implementation specific + {ERR => "$prog: cannot get niceness for pid `1073741824'" }], + + # valid absolute priority + ['pid-4', '1', $pid, {EXIT=>0}], + + # invalid absolute priority + ['pid-5', '0', $pid, {EXIT=>1}, {ERR_SUBST => 's/:[^:]+$//'}, + {ERR => "$prog: cannot set niceness for pid `$pid'" }], + + # valid relative priority + ['pid-6', -n, '1', $pid, {EXIT=>0}], + + # invalid relative priority + ['pid-7', '-n', '-1', $pid, {EXIT=>1}, {ERR_SUBST => 's/:[^:]+$//'}, + {ERR => "$prog: cannot set niceness for pid `$pid'" }], + + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, address@hidden, $save_temps, $verbose); + +kill $pid; + +exit $fail; +EOF -- 1.5.4.3