1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297 |
- /*++
- Copyright (c) 2015 Minoca Corp. All Rights Reserved
- Module Name:
- ssdaemon.c
- Abstract:
- This module implements the start-stop-daemon command, which is used,
- predictably, for starting and stopping daemons.
- Author:
- Evan Green 23-Mar-2015
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <libgen.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "swlib.h"
- #include "login/lutil.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define SS_DAEMON_VERSION_MAJOR 1
- #define SS_DAEMON_VERSION_MINOR 0
- #define SS_DAEMON_USAGE \
- "usage: start-stop-daemon [options] command\n" \
- "The start-stop-daemon utility is used control system-level processes.\n" \
- "This utility can find existing instances of a running process, spawn, \n" \
- "or terminate processes. Options are\n" \
- " -S, --start -- Start the given command if it is not already running.\n" \
- " -K, --stop -- Signal the specified process and exit.\n" \
- "Matching options:\n" \
- " -p, --pidfile=file -- Check whether a process has created a pidfile.\n" \
- " -x, --exec=executable -- Check for processes that are an instance of \n"\
- " the given executable. This should be an absolute path.\n" \
- " -n, --name=name -- Check for processes that match the given name.\n" \
- " -u, --user=user -- Check for processes owned by the given user.\n" \
- "Generic options:\n" \
- " -g, --group=group -- Change to the given group when starting.\n" \
- " -s, --signal=signal -- Specify the signal to use to stop a process.\n" \
- " The default is TERM.\n" \
- " -a, --startas=path -- Start the process specified. The default is the\n"\
- " argument given by --exec.\n" \
- " -t, --test -- Print actions that would occur but do nothing.\n" \
- " -o, --oknodo -- Exit with status 0 instead of 1 if no actions are \n" \
- " or would be taken.\n" \
- " -q, --quiet -- Display only error messages.\n" \
- " -c, --chuid=user[:group] -- Change to the given user/group before \n" \
- " starting the process.\n" \
- " -r, --chroot=root -- Change to the given root before operating.\n" \
- " -d, --chdir=dir -- Change to the given working directory before \n" \
- " operating.\n" \
- " -b, --background -- Force the application into the background by \n" \
- " forking twice.\n" \
- " -C, --no-close -- Don't close file descriptors when forcing a \n" \
- " daemon into the background.\n" \
- " -N, --nicelevel=nice -- Alter the priority of the starting process.\n" \
- " -k, --umask=mask -- Sets the umask before starting the process.\n" \
- " -m, --make-pidfile -- Create the pidfile specified by --pidfile right\n"\
- " before execing the process. This file will not be removed when \n" \
- " the process exits.\n" \
- " -v, --verbose -- Print more messages.\n" \
- " --help -- Displays this help text and exits.\n" \
- " --version -- Displays the application version and exits.\n"
- #define SS_DAEMON_OPTIONS_STRING "SKp:x:n:u:g:s:a:toqc:r:d:bCN:k:mvHV"
- //
- // Define application options.
- //
- //
- // Set this option to run a start action.
- //
- #define SS_DAEMON_OPTION_START 0x00000001
- //
- // Set this option to run a stop action.
- //
- #define SS_DAEMON_OPTION_STOP 0x00000002
- //
- // Set this option to test, but not actually do anything.
- //
- #define SS_DAEMON_OPTION_TEST 0x00000004
- //
- // Set this option to return successfully if no actions were performed.
- //
- #define SS_DAEMON_OPTION_NOTHING_OK 0x00000008
- //
- // Set this option to be quiet.
- //
- #define SS_DAEMON_OPTION_QUIET 0x00000010
- //
- // Set this option to be loud.
- //
- #define SS_DAEMON_OPTION_VERBOSE 0x00000020
- //
- // Set this option for force background the starting app.
- //
- #define SS_DAEMON_OPTION_BACKGROUND 0x00000040
- //
- // Set this option to not close file descriptors when backgrounding.
- //
- #define SS_DAEMON_OPTION_NO_CLOSE 0x00000080
- //
- // Set this option to make a pidfile.
- //
- #define SS_DAEMON_OPTION_MAKE_PIDFILE 0x00000100
- //
- // Set this option if the caller wanted to change the nice level.
- //
- #define SS_DAEMON_OPTION_NICE 0x00000200
- //
- // Define the number of initial elements in the match ID array.
- //
- #define SS_DAEMON_INITIAL_MATCH_ARRAY_SIZE 32
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure stores the application context for the start-stop-daemon
- utility.
- Members:
- Options - Stores a bitfield of application options. See SS_DAEMON_OPTION_*
- definitions.
- ChangeGroup - Stores the group ID to change to, or -1 if unspecified.
- ChangeUser - Stores the user ID to change to, or -1 if unspecified.
- ExecPath - Stores the path to execute to start the daemon.
- MatchUserId - Stores the user ID to match against when stopping, or -1 if
- unspecified.
- NiceLevel - Stores the nice level to use when starting, or -1 if
- unspecified.
- PidFilePath - Stores an optional pointer to the path to the pid file.
- MatchProcessIds - Stores an array of process IDs that match the criteria.
- MatchProcessIdCount - Stores the number of valid elements in the match
- process IDs array.
- MatchProcessIdCapacity - Stores the buffer size in elements of the match
- process IDs array.
- ProcessId - Stores the process ID loaded from the pid file.
- ProcessName - Stores the process name to match against.
- RootDirectory - Stores the root directory to switch to before operating.
- Signal - Stores the signal to send to stop a daemon.
- StartAs - Stores the first argument to the exec functions, which may be
- different than the exec path.
- Umask - Stores the umask to set, or -1 if unspecified.
- WorkingDirectory - Stores the optional working directory to change to
- before starting.
- --*/
- typedef struct _SS_DAEMON_CONTEXT {
- INT Options;
- gid_t ChangeGroup;
- uid_t ChangeUser;
- PSTR ExecPath;
- uid_t MatchUserId;
- long NiceLevel;
- PSTR PidFilePath;
- pid_t *MatchProcessIds;
- UINTN MatchProcessIdCount;
- UINTN MatchProcessIdCapacity;
- PSTR ProcessName;
- PSTR RootDirectory;
- INT Signal;
- PSTR StartAs;
- long Umask;
- PSTR WorkingDirectory;
- } SS_DAEMON_CONTEXT, *PSS_DAEMON_CONTEXT;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- INT
- SsDaemonReadPidFile (
- PSS_DAEMON_CONTEXT Context
- );
- VOID
- SsDaemonWritePidFile (
- PSS_DAEMON_CONTEXT Context
- );
- INT
- SsDaemonMatchAllProcesses (
- PSS_DAEMON_CONTEXT Context
- );
- VOID
- SsDaemonMatchPid (
- PSS_DAEMON_CONTEXT Context,
- pid_t ProcessId
- );
- INT
- SsDaemonStop (
- PSS_DAEMON_CONTEXT Context
- );
- VOID
- SsDaemonPrintStopDescription (
- PSS_DAEMON_CONTEXT Context
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option SsDaemonLongOptions[] = {
- {"start", no_argument, 0, 'S'},
- {"stop", no_argument, 0, 'K'},
- {"pidfile", required_argument, 0, 'p'},
- {"exec", required_argument, 0, 'x'},
- {"name", required_argument, 0, 'n'},
- {"user", required_argument, 0, 'u'},
- {"group", required_argument, 0, 'g'},
- {"signal", required_argument, 0, 's'},
- {"startas", required_argument, 0, 'a'},
- {"test", no_argument, 0, 't'},
- {"oknodo", no_argument, 0, 'o'},
- {"quiet", no_argument, 0, 'q'},
- {"chuid", required_argument, 0, 'c'},
- {"chroot", required_argument, 0, 'r'},
- {"chdir", required_argument, 0, 'd'},
- {"background", no_argument, 0, 'b'},
- {"no-close", no_argument, 0, 'C'},
- {"nicelevel", required_argument, 0, 'N'},
- {"umask", required_argument, 0, 'k'},
- {"make-pidfile", no_argument, 0, 'm'},
- {"verbose", no_argument, 0, 'v'},
- {"help", no_argument, 0, 'H'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- SsDaemonMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the start-stop-daemon utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- INT ActionCount;
- PSTR AfterScan;
- ULONG ArgumentIndex;
- pid_t Child;
- SS_DAEMON_CONTEXT Context;
- int DevNull;
- gid_t DummyGroup;
- PSTR *NewArguments;
- INT Option;
- ULONG Options;
- int Status;
- struct passwd *User;
- memset(&Context, 0, sizeof(SS_DAEMON_CONTEXT));
- Context.ChangeGroup = -1;
- Context.ChangeUser = -1;
- Context.MatchUserId = -1;
- Context.NiceLevel = -1;
- Context.Signal = SIGTERM;
- Context.Umask = -1;
- NewArguments = NULL;
- Options = 0;
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- SS_DAEMON_OPTIONS_STRING,
- SsDaemonLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'S':
- if ((Options & SS_DAEMON_OPTION_STOP) != 0) {
- SwPrintError(0,
- NULL,
- "Exactly one of --start or --stop must be "
- "specified.");
- Status = 1;
- goto MainEnd;
- }
- Options |= SS_DAEMON_OPTION_START;
- break;
- case 'K':
- if ((Options & SS_DAEMON_OPTION_START) != 0) {
- SwPrintError(0,
- NULL,
- "Exactly one of --start or --stop must be "
- "specified.");
- Status = 1;
- goto MainEnd;
- }
- Options |= SS_DAEMON_OPTION_STOP;
- break;
- case 'p':
- Context.PidFilePath = optarg;
- break;
- case 'x':
- Context.ExecPath = optarg;
- break;
- case 'n':
- Context.ProcessName = optarg;
- break;
- case 'u':
- Status = SwGetUserIdFromName(optarg, &(Context.MatchUserId));
- if (Status != 0) {
- Context.MatchUserId = strtoul(optarg, &AfterScan, 10);
- if (AfterScan == optarg) {
- SwPrintError(0, optarg, "Invalid user");
- goto MainEnd;
- }
- }
- break;
- case 'g':
- Status = SwGetGroupIdFromName(optarg, &(Context.ChangeGroup));
- if (Status != 0) {
- Context.ChangeGroup = strtoul(optarg, &AfterScan, 10);
- if (AfterScan == optarg) {
- SwPrintError(0, optarg, "Invalid group");
- goto MainEnd;
- }
- }
- break;
- case 's':
- Context.Signal = SwGetSignalNumberFromName(optarg);
- if (Context.Signal == -1) {
- SwPrintError(0, optarg, "Invalid signal");
- Status = EINVAL;
- goto MainEnd;
- }
- break;
- case 'a':
- Context.StartAs = optarg;
- break;
- case 't':
- Options |= SS_DAEMON_OPTION_TEST;
- break;
- case 'o':
- Options |= SS_DAEMON_OPTION_NOTHING_OK;
- break;
- case 'q':
- Options |= SS_DAEMON_OPTION_QUIET;
- Options &= ~SS_DAEMON_OPTION_VERBOSE;
- break;
- case 'v':
- Options |= SS_DAEMON_OPTION_VERBOSE;
- Options &= ~SS_DAEMON_OPTION_QUIET;
- break;
- case 'c':
- Status = SwParseUserAndGroupString(optarg,
- &(Context.ChangeUser),
- &DummyGroup);
- if (Status != 0) {
- SwPrintError(0, optarg, "Invalid user:group");
- goto MainEnd;
- }
- if (DummyGroup != -1) {
- if (Context.ChangeGroup == -1) {
- Context.ChangeGroup = DummyGroup;
- }
- }
- break;
- case 'r':
- Context.RootDirectory = optarg;
- break;
- case 'd':
- Context.WorkingDirectory = optarg;
- break;
- case 'b':
- Options |= SS_DAEMON_OPTION_BACKGROUND;
- break;
- case 'C':
- Options |= SS_DAEMON_OPTION_NO_CLOSE;
- break;
- case 'N':
- Context.NiceLevel = strtoul(optarg, &AfterScan, 10);
- if (AfterScan == optarg) {
- SwPrintError(0, optarg, "Invalid nice level");
- Status = EINVAL;
- goto MainEnd;
- }
- Options |= SS_DAEMON_OPTION_NICE;
- break;
- case 'k':
- Context.Umask = strtoul(optarg, &AfterScan, 8);
- if (AfterScan == optarg) {
- SwPrintError(0, optarg, "Invalid umask");
- Status = EINVAL;
- goto MainEnd;
- }
- break;
- case 'm':
- Options |= SS_DAEMON_OPTION_MAKE_PIDFILE;
- break;
- case 'V':
- SwPrintVersion(SS_DAEMON_VERSION_MAJOR, SS_DAEMON_VERSION_MINOR);
- return 1;
- case 'H':
- printf(SS_DAEMON_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- Context.Options = Options;
- ArgumentIndex = optind;
- if (ArgumentIndex > ArgumentCount) {
- ArgumentIndex = ArgumentCount;
- }
- //
- // At least one of start or stop is required.
- //
- if ((Options & (SS_DAEMON_OPTION_STOP | SS_DAEMON_OPTION_START)) == 0) {
- SwPrintError(0,
- NULL,
- "Exactly one of --start or --stop must be specified.");
- Status = 1;
- goto MainEnd;
- }
- //
- // A pidfile is required if the caller wants to make one.
- //
- if (((Options & SS_DAEMON_OPTION_MAKE_PIDFILE) != 0) &&
- (Context.PidFilePath == NULL)) {
- SwPrintError(0, NULL, "-p is required with -m");
- Status = EINVAL;
- goto MainEnd;
- }
- //
- // Check stop requirements.
- //
- if ((Options & SS_DAEMON_OPTION_STOP) != 0) {
- if ((Context.ExecPath == NULL) &&
- (Context.PidFilePath == NULL) &&
- (Context.MatchUserId == -1) &&
- (Context.ProcessName == NULL)) {
- SwPrintError(0, NULL, "At least one of -xpun is required with -K");
- Status = EINVAL;
- goto MainEnd;
- }
- } else if ((Options & SS_DAEMON_OPTION_START) != 0) {
- if ((Context.ExecPath == NULL) && (Context.StartAs == NULL)) {
- SwPrintError(0, NULL, "At least one of -xa is required for -S");
- Status = EINVAL;
- goto MainEnd;
- }
- //
- // At least one of start or stop is required.
- //
- } else {
- SwPrintError(0,
- NULL,
- "Exactly one of --start or --stop must be specified.");
- Status = EINVAL;
- goto MainEnd;
- }
- //
- // If no exec name was given but a start-as was, use that as the exec name.
- //
- if (Context.StartAs == NULL) {
- Context.StartAs = Context.ExecPath;
- }
- if ((Context.ExecPath == NULL) && (Context.StartAs != NULL)) {
- Context.ExecPath = Context.StartAs;
- }
- //
- // Chroot if requested.
- //
- if (Context.RootDirectory != NULL) {
- Status = chroot(Context.RootDirectory);
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status, Context.RootDirectory, "Failed to chroot");
- goto MainEnd;
- }
- Status = chdir("/");
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status, Context.RootDirectory, "Failed to chdir");
- goto MainEnd;
- }
- }
- //
- // Change directories if requested too.
- //
- if (Context.WorkingDirectory != NULL) {
- Status = chdir(Context.WorkingDirectory);
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status,
- Context.WorkingDirectory,
- "Failed to change directory");
- goto MainEnd;
- }
- }
- //
- // Open up the pid file if specified.
- //
- if (Context.PidFilePath != NULL) {
- Status = SsDaemonReadPidFile(&Context);
- if (Status != 0) {
- SwPrintError(0, Context.PidFilePath, "Failed to read");
- goto MainEnd;
- }
- } else {
- Status = SsDaemonMatchAllProcesses(&Context);
- if (Status != 0) {
- SwPrintError(0, NULL, "Failed to get process list");
- }
- }
- //
- // Perform stop actions if this is a stop request.
- //
- if ((Options & SS_DAEMON_OPTION_STOP) != 0) {
- ActionCount = SsDaemonStop(&Context);
- if (ActionCount == 0) {
- if ((Options & SS_DAEMON_OPTION_NOTHING_OK) != 0) {
- Status = 0;
- } else {
- Status = 1;
- }
- } else {
- Status = 0;
- }
- goto MainEnd;
- }
- //
- // This is a start operation. If something matches already, do nothing.
- //
- if (Context.MatchProcessIdCount != 0) {
- if ((Options & SS_DAEMON_OPTION_QUIET) == 0) {
- printf("%s is already running with pid %lu\n",
- Context.ExecPath,
- Context.MatchProcessIds[0]);
- }
- Status = 1;
- if ((Options & SS_DAEMON_OPTION_NOTHING_OK) != 0) {
- Status = 0;
- }
- goto MainEnd;
- }
- //
- // Create the new arguments array.
- //
- assert(ArgumentIndex != 0);
- ArgumentIndex -= 1;
- NewArguments = malloc(sizeof(PSTR) * (ArgumentCount - ArgumentIndex + 1));
- if (NewArguments == NULL) {
- Status = ENOMEM;
- goto MainEnd;
- }
- memcpy(NewArguments,
- &(Arguments[ArgumentIndex]),
- sizeof(PSTR) * (ArgumentCount - ArgumentIndex + 1));
- NewArguments[0] = Context.StartAs;
- //
- // Get into the background if requested.
- //
- if ((Options & SS_DAEMON_OPTION_BACKGROUND) != 0) {
- //
- // Fork and exit in the parent, continue in the child.
- //
- Child = fork();
- if (Child < 0) {
- Status = errno;
- SwPrintError(Status, NULL, "Failed to fork");
- goto MainEnd;
- }
- //
- // Exit immediately in the parent, continue in the child.
- //
- if (Child > 0) {
- Status = 0;
- goto MainEnd;
- }
- //
- // Become a session leader, detaching from the controlling terminal.
- //
- setsid();
- //
- // Point standard in, out, and error at /dev/null.
- //
- if ((Options & SS_DAEMON_OPTION_NO_CLOSE) == 0) {
- DevNull = open("/dev/null", O_RDWR);
- if (DevNull >= 0) {
- dup2(DevNull, STDIN_FILENO);
- dup2(DevNull, STDOUT_FILENO);
- dup2(DevNull, STDERR_FILENO);
- close(DevNull);
- }
- SwCloseFrom(STDERR_FILENO + 1);
- }
- //
- // Double fork. Do this to prevent the grandchild process from ever
- // acquiring a controlling terminal. Since only the session leader
- // can acquire a controlling terminal, after the fork its new PID
- // will not be the session leader ID.
- //
- Child = fork();
- if (Child < 0) {
- exit(1);
- } else if (Child != 0) {
- exit(0);
- }
- //
- // The remainder now runs as the grandchild.
- //
- }
- //
- // Write the pid file.
- //
- if ((Options & SS_DAEMON_OPTION_MAKE_PIDFILE) != 0) {
- SsDaemonWritePidFile(&Context);
- }
- //
- // Change identity if requested.
- //
- if (Context.ChangeUser != -1) {
- User = getpwuid(Context.ChangeUser);
- if (Context.ChangeGroup != -1) {
- User->pw_gid = Context.ChangeGroup;
- }
- SwBecomeUser(User);
- } else if (Context.ChangeGroup != -1) {
- Status = setgid(Context.ChangeGroup);
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status, NULL, "setgid failed");
- goto MainEnd;
- }
- setgroups(1, &(Context.ChangeGroup));
- }
- //
- // Change nice level if requested.
- //
- if ((Options & SS_DAEMON_OPTION_NICE) != 0) {
- Status = nice(Context.NiceLevel);
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status, NULL, "nice failed");
- goto MainEnd;
- }
- }
- //
- // Change the umask if requested.
- //
- if (Context.Umask != -1) {
- umask(Context.Umask);
- }
- //
- // Make it rain.
- //
- execvp(NewArguments[0], NewArguments);
- Status = errno;
- SwPrintError(Status, Arguments[ArgumentIndex], "Cannot execute");
- MainEnd:
- if (NewArguments != NULL) {
- free(NewArguments);
- }
- if (Context.MatchProcessIds != NULL) {
- free(Context.MatchProcessIds);
- }
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- INT
- SsDaemonReadPidFile (
- PSS_DAEMON_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine tries to read a pid file. It is not an error if the file does
- not exist.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- 0 on success or if the file does not exist.
- Non-zero on any other failures.
- --*/
- {
- FILE *File;
- unsigned int ScannedPid;
- File = fopen(Context->PidFilePath, "r");
- if (File == NULL) {
- if (errno == ENOENT) {
- return 0;
- }
- SwPrintError(errno, Context->PidFilePath, "Cannot open");
- return errno;
- }
- if (fscanf(File, "%u", &ScannedPid) != 1) {
- return EINVAL;
- }
- SsDaemonMatchPid(Context, ScannedPid);
- return 0;
- }
- VOID
- SsDaemonWritePidFile (
- PSS_DAEMON_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine tries to write the current process ID to a pid file.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- None. At this point it's too late for errors.
- --*/
- {
- CHAR Buffer[64];
- ssize_t BytesWritten;
- int File;
- int Size;
- size_t TotalBytesWritten;
- File = open(Context->PidFilePath, O_WRONLY | O_TRUNC | O_CREAT, 0644);
- if (File >= 0) {
- Size = snprintf(Buffer,
- sizeof(Buffer),
- "%lu",
- (long unsigned int)getpid());
- TotalBytesWritten = 0;
- do {
- BytesWritten = write(File,
- Buffer + TotalBytesWritten,
- Size - TotalBytesWritten);
- if (BytesWritten <= 0) {
- if (errno == EINTR) {
- continue;
- }
- break;
- }
- TotalBytesWritten += BytesWritten;
- } while (TotalBytesWritten < Size);
- close(File);
- }
- return;
- }
- INT
- SsDaemonMatchAllProcesses (
- PSS_DAEMON_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine reads in all the current processes.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- size_t Count;
- UINTN Index;
- pid_t *ProcessIds;
- size_t Size;
- INT Status;
- ProcessIds = NULL;
- Size = 0;
- SwGetProcessIdList(NULL, &Size);
- if (Size == 0) {
- return ESRCH;
- }
- //
- // Add a fudge factor in case more processes come in.
- //
- Size *= 2;
- ProcessIds = malloc(Size);
- if (ProcessIds == NULL) {
- return ENOMEM;
- }
- memset(ProcessIds, 0, Size);
- Status = SwGetProcessIdList(ProcessIds, &Size);
- if (Status != 0) {
- SwPrintError(0, NULL, "Failed to get process ID list");
- goto GetAllProcessesEnd;
- }
- Count = Size / sizeof(pid_t);
- for (Index = 0; Index < Count; Index += 1) {
- SsDaemonMatchPid(Context, ProcessIds[Index]);
- }
- Status = 0;
- GetAllProcessesEnd:
- if (ProcessIds != NULL) {
- free(ProcessIds);
- }
- return Status;
- }
- VOID
- SsDaemonMatchPid (
- PSS_DAEMON_CONTEXT Context,
- pid_t ProcessId
- )
- /*++
- Routine Description:
- This routine matches a given process ID against the user specified criteria.
- If the given process ID matches, it will be added to the context's array
- of matching processes.
- Arguments:
- Context - Supplies a pointer to the application context.
- ProcessId - Supplies the process ID to match against.
- Return Value:
- None.
- --*/
- {
- PSTR BaseName;
- BOOL Match;
- PVOID NewBuffer;
- UINTN NewCapacity;
- PSWISS_PROCESS_INFORMATION ProcessInformation;
- int Status;
- Status = SwGetProcessInformation(ProcessId, &ProcessInformation);
- if (Status != 0) {
- return;
- }
- Match = TRUE;
- if (Context->ExecPath != NULL) {
- if (strcmp(ProcessInformation->Name, Context->ExecPath) != 0) {
- Match = FALSE;
- }
- }
- if ((Match != FALSE) && (Context->ProcessName != NULL)) {
- BaseName = basename(ProcessInformation->Name);
- if (strcmp(BaseName, Context->ProcessName) != 0) {
- Match = FALSE;
- }
- }
- if ((Match != FALSE) && (Context->MatchUserId != -1)) {
- if (ProcessInformation->RealUserId != Context->MatchUserId) {
- Match = FALSE;
- }
- }
- SwDestroyProcessInformation(ProcessInformation);
- if (Match == FALSE) {
- return;
- }
- //
- // Add the process to the match array.
- //
- if (Context->MatchProcessIdCount >= Context->MatchProcessIdCapacity) {
- if (Context->MatchProcessIdCount == 0) {
- NewCapacity = SS_DAEMON_INITIAL_MATCH_ARRAY_SIZE;
- } else {
- NewCapacity = Context->MatchProcessIdCapacity * 2;
- }
- assert(NewCapacity > Context->MatchProcessIdCount + 1);
- NewBuffer = realloc(Context->MatchProcessIds,
- NewCapacity * sizeof(pid_t));
- if (NewBuffer == NULL) {
- //
- // Quietly ignore allocation failures.
- //
- return;
- }
- Context->MatchProcessIds = NewBuffer;
- Context->MatchProcessIdCapacity = NewCapacity;
- }
- Context->MatchProcessIds[Context->MatchProcessIdCount] = ProcessId;
- Context->MatchProcessIdCount += 1;
- return;
- }
- INT
- SsDaemonStop (
- PSS_DAEMON_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine performs stop actions on matchind PIDs.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- Returns the number of actions performed.
- --*/
- {
- UINTN Index;
- PSTR Plural;
- INT ProcessesKilled;
- INT Signal;
- ProcessesKilled = 0;
- if (Context->MatchProcessIdCount == 0) {
- SsDaemonPrintStopDescription(Context);
- printf(": No processes found\n");
- goto StopEnd;
- }
- for (Index = 0; Index < Context->MatchProcessIdCount; Index += 1) {
- Signal = Context->Signal;
- if ((Context->Options & SS_DAEMON_OPTION_TEST) != 0) {
- Signal = 0;
- }
- if (kill(Context->MatchProcessIds[Index], Signal) == 0) {
- ProcessesKilled += 1;
- } else {
- SwPrintError(errno,
- NULL,
- "Failed to kill process %lu",
- (long unsigned int)(Context->MatchProcessIds[Index]));
- Context->MatchProcessIds[Index] = 0;
- if ((Context->Options & SS_DAEMON_OPTION_TEST) != 0) {
- ProcessesKilled = -1;
- goto StopEnd;
- }
- }
- }
- if ((ProcessesKilled > 0) &&
- ((Context->Options & SS_DAEMON_OPTION_QUIET) == 0)) {
- printf("Stopped ");
- SsDaemonPrintStopDescription(Context);
- Plural = "";
- if (ProcessesKilled > 1) {
- Plural = "s";
- }
- printf(" (pid%s", Plural);
- for (Index = 0; Index < Context->MatchProcessIdCount; Index += 1) {
- if (Context->MatchProcessIds[Index] != 0) {
- printf(" %lu",
- (long unsigned int)(Context->MatchProcessIds[Index]));
- }
- }
- printf(")\n");
- }
- StopEnd:
- return ProcessesKilled;
- }
- VOID
- SsDaemonPrintStopDescription (
- PSS_DAEMON_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine prints to standard out a description of what is being stopped,
- without a newline.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- None.
- --*/
- {
- PSTR Space;
- Space = "";
- if (Context->ProcessName != NULL) {
- printf("%s", Context->ProcessName);
- Space = " ";
- }
- if (Context->ExecPath != NULL) {
- printf("%s%s", Space, Context->ExecPath);
- Space = " ";
- }
- if (Context->PidFilePath != NULL) {
- printf("%sprocess in pid file '%s'", Space, Context->PidFilePath);
- Space = " ";
- }
- if (Context->MatchUserId != -1) {
- printf("%sprocesses owned by user %ld",
- Space,
- (long int)Context->MatchUserId);
- }
- return;
- }
|