ssdaemon.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. ssdaemon.c
  5. Abstract:
  6. This module implements the start-stop-daemon command, which is used,
  7. predictably, for starting and stopping daemons.
  8. Author:
  9. Evan Green 23-Mar-2015
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <getopt.h>
  21. #include <libgen.h>
  22. #include <signal.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <unistd.h>
  26. #include "swlib.h"
  27. #include "login/lutil.h"
  28. //
  29. // ---------------------------------------------------------------- Definitions
  30. //
  31. #define SS_DAEMON_VERSION_MAJOR 1
  32. #define SS_DAEMON_VERSION_MINOR 0
  33. #define SS_DAEMON_USAGE \
  34. "usage: start-stop-daemon [options] command\n" \
  35. "The start-stop-daemon utility is used control system-level processes.\n" \
  36. "This utility can find existing instances of a running process, spawn, \n" \
  37. "or terminate processes. Options are\n" \
  38. " -S, --start -- Start the given command if it is not already running.\n" \
  39. " -K, --stop -- Signal the specified process and exit.\n" \
  40. "Matching options:\n" \
  41. " -p, --pidfile=file -- Check whether a process has created a pidfile.\n" \
  42. " -x, --exec=executable -- Check for processes that are an instance of \n"\
  43. " the given executable. This should be an absolute path.\n" \
  44. " -n, --name=name -- Check for processes that match the given name.\n" \
  45. " -u, --user=user -- Check for processes owned by the given user.\n" \
  46. "Generic options:\n" \
  47. " -g, --group=group -- Change to the given group when starting.\n" \
  48. " -s, --signal=signal -- Specify the signal to use to stop a process.\n" \
  49. " The default is TERM.\n" \
  50. " -a, --startas=path -- Start the process specified. The default is the\n"\
  51. " argument given by --exec.\n" \
  52. " -t, --test -- Print actions that would occur but do nothing.\n" \
  53. " -o, --oknodo -- Exit with status 0 instead of 1 if no actions are \n" \
  54. " or would be taken.\n" \
  55. " -q, --quiet -- Display only error messages.\n" \
  56. " -c, --chuid=user[:group] -- Change to the given user/group before \n" \
  57. " starting the process.\n" \
  58. " -r, --chroot=root -- Change to the given root before operating.\n" \
  59. " -d, --chdir=dir -- Change to the given working directory before \n" \
  60. " operating.\n" \
  61. " -b, --background -- Force the application into the background by \n" \
  62. " forking twice.\n" \
  63. " -C, --no-close -- Don't close file descriptors when forcing a \n" \
  64. " daemon into the background.\n" \
  65. " -N, --nicelevel=nice -- Alter the priority of the starting process.\n" \
  66. " -k, --umask=mask -- Sets the umask before starting the process.\n" \
  67. " -m, --make-pidfile -- Create the pidfile specified by --pidfile right\n"\
  68. " before execing the process. This file will not be removed when \n" \
  69. " the process exits.\n" \
  70. " -v, --verbose -- Print more messages.\n" \
  71. " --help -- Displays this help text and exits.\n" \
  72. " --version -- Displays the application version and exits.\n"
  73. #define SS_DAEMON_OPTIONS_STRING "SKp:x:n:u:g:s:a:toqc:r:d:bCN:k:mvHV"
  74. //
  75. // Define application options.
  76. //
  77. //
  78. // Set this option to run a start action.
  79. //
  80. #define SS_DAEMON_OPTION_START 0x00000001
  81. //
  82. // Set this option to run a stop action.
  83. //
  84. #define SS_DAEMON_OPTION_STOP 0x00000002
  85. //
  86. // Set this option to test, but not actually do anything.
  87. //
  88. #define SS_DAEMON_OPTION_TEST 0x00000004
  89. //
  90. // Set this option to return successfully if no actions were performed.
  91. //
  92. #define SS_DAEMON_OPTION_NOTHING_OK 0x00000008
  93. //
  94. // Set this option to be quiet.
  95. //
  96. #define SS_DAEMON_OPTION_QUIET 0x00000010
  97. //
  98. // Set this option to be loud.
  99. //
  100. #define SS_DAEMON_OPTION_VERBOSE 0x00000020
  101. //
  102. // Set this option for force background the starting app.
  103. //
  104. #define SS_DAEMON_OPTION_BACKGROUND 0x00000040
  105. //
  106. // Set this option to not close file descriptors when backgrounding.
  107. //
  108. #define SS_DAEMON_OPTION_NO_CLOSE 0x00000080
  109. //
  110. // Set this option to make a pidfile.
  111. //
  112. #define SS_DAEMON_OPTION_MAKE_PIDFILE 0x00000100
  113. //
  114. // Set this option if the caller wanted to change the nice level.
  115. //
  116. #define SS_DAEMON_OPTION_NICE 0x00000200
  117. //
  118. // Define the number of initial elements in the match ID array.
  119. //
  120. #define SS_DAEMON_INITIAL_MATCH_ARRAY_SIZE 32
  121. //
  122. // ------------------------------------------------------ Data Type Definitions
  123. //
  124. /*++
  125. Structure Description:
  126. This structure stores the application context for the start-stop-daemon
  127. utility.
  128. Members:
  129. Options - Stores a bitfield of application options. See SS_DAEMON_OPTION_*
  130. definitions.
  131. ChangeGroup - Stores the group ID to change to, or -1 if unspecified.
  132. ChangeUser - Stores the user ID to change to, or -1 if unspecified.
  133. ExecPath - Stores the path to execute to start the daemon.
  134. MatchUserId - Stores the user ID to match against when stopping, or -1 if
  135. unspecified.
  136. NiceLevel - Stores the nice level to use when starting, or -1 if
  137. unspecified.
  138. PidFilePath - Stores an optional pointer to the path to the pid file.
  139. MatchProcessIds - Stores an array of process IDs that match the criteria.
  140. MatchProcessIdCount - Stores the number of valid elements in the match
  141. process IDs array.
  142. MatchProcessIdCapacity - Stores the buffer size in elements of the match
  143. process IDs array.
  144. ProcessId - Stores the process ID loaded from the pid file.
  145. ProcessName - Stores the process name to match against.
  146. RootDirectory - Stores the root directory to switch to before operating.
  147. Signal - Stores the signal to send to stop a daemon.
  148. StartAs - Stores the first argument to the exec functions, which may be
  149. different than the exec path.
  150. Umask - Stores the umask to set, or -1 if unspecified.
  151. WorkingDirectory - Stores the optional working directory to change to
  152. before starting.
  153. --*/
  154. typedef struct _SS_DAEMON_CONTEXT {
  155. INT Options;
  156. gid_t ChangeGroup;
  157. uid_t ChangeUser;
  158. PSTR ExecPath;
  159. uid_t MatchUserId;
  160. long NiceLevel;
  161. PSTR PidFilePath;
  162. pid_t *MatchProcessIds;
  163. UINTN MatchProcessIdCount;
  164. UINTN MatchProcessIdCapacity;
  165. PSTR ProcessName;
  166. PSTR RootDirectory;
  167. INT Signal;
  168. PSTR StartAs;
  169. long Umask;
  170. PSTR WorkingDirectory;
  171. } SS_DAEMON_CONTEXT, *PSS_DAEMON_CONTEXT;
  172. //
  173. // ----------------------------------------------- Internal Function Prototypes
  174. //
  175. INT
  176. SsDaemonReadPidFile (
  177. PSS_DAEMON_CONTEXT Context
  178. );
  179. VOID
  180. SsDaemonWritePidFile (
  181. PSS_DAEMON_CONTEXT Context
  182. );
  183. INT
  184. SsDaemonMatchAllProcesses (
  185. PSS_DAEMON_CONTEXT Context
  186. );
  187. VOID
  188. SsDaemonMatchPid (
  189. PSS_DAEMON_CONTEXT Context,
  190. pid_t ProcessId
  191. );
  192. INT
  193. SsDaemonStop (
  194. PSS_DAEMON_CONTEXT Context
  195. );
  196. VOID
  197. SsDaemonPrintStopDescription (
  198. PSS_DAEMON_CONTEXT Context
  199. );
  200. //
  201. // -------------------------------------------------------------------- Globals
  202. //
  203. struct option SsDaemonLongOptions[] = {
  204. {"start", no_argument, 0, 'S'},
  205. {"stop", no_argument, 0, 'K'},
  206. {"pidfile", required_argument, 0, 'p'},
  207. {"exec", required_argument, 0, 'x'},
  208. {"name", required_argument, 0, 'n'},
  209. {"user", required_argument, 0, 'u'},
  210. {"group", required_argument, 0, 'g'},
  211. {"signal", required_argument, 0, 's'},
  212. {"startas", required_argument, 0, 'a'},
  213. {"test", no_argument, 0, 't'},
  214. {"oknodo", no_argument, 0, 'o'},
  215. {"quiet", no_argument, 0, 'q'},
  216. {"chuid", required_argument, 0, 'c'},
  217. {"chroot", required_argument, 0, 'r'},
  218. {"chdir", required_argument, 0, 'd'},
  219. {"background", no_argument, 0, 'b'},
  220. {"no-close", no_argument, 0, 'C'},
  221. {"nicelevel", required_argument, 0, 'N'},
  222. {"umask", required_argument, 0, 'k'},
  223. {"make-pidfile", no_argument, 0, 'm'},
  224. {"verbose", no_argument, 0, 'v'},
  225. {"help", no_argument, 0, 'H'},
  226. {"version", no_argument, 0, 'V'},
  227. {NULL, 0, 0, 0},
  228. };
  229. //
  230. // ------------------------------------------------------------------ Functions
  231. //
  232. INT
  233. SsDaemonMain (
  234. INT ArgumentCount,
  235. CHAR **Arguments
  236. )
  237. /*++
  238. Routine Description:
  239. This routine is the main entry point for the start-stop-daemon utility.
  240. Arguments:
  241. ArgumentCount - Supplies the number of command line arguments the program
  242. was invoked with.
  243. Arguments - Supplies a tokenized array of command line arguments.
  244. Return Value:
  245. Returns an integer exit code. 0 for success, nonzero otherwise.
  246. --*/
  247. {
  248. INT ActionCount;
  249. PSTR AfterScan;
  250. ULONG ArgumentIndex;
  251. pid_t Child;
  252. SS_DAEMON_CONTEXT Context;
  253. int DevNull;
  254. gid_t DummyGroup;
  255. PSTR *NewArguments;
  256. INT Option;
  257. ULONG Options;
  258. int Status;
  259. struct passwd *User;
  260. memset(&Context, 0, sizeof(SS_DAEMON_CONTEXT));
  261. Context.ChangeGroup = -1;
  262. Context.ChangeUser = -1;
  263. Context.MatchUserId = -1;
  264. Context.NiceLevel = -1;
  265. Context.Signal = SIGTERM;
  266. Context.Umask = -1;
  267. NewArguments = NULL;
  268. Options = 0;
  269. //
  270. // Process the control arguments.
  271. //
  272. while (TRUE) {
  273. Option = getopt_long(ArgumentCount,
  274. Arguments,
  275. SS_DAEMON_OPTIONS_STRING,
  276. SsDaemonLongOptions,
  277. NULL);
  278. if (Option == -1) {
  279. break;
  280. }
  281. if ((Option == '?') || (Option == ':')) {
  282. Status = 1;
  283. goto MainEnd;
  284. }
  285. switch (Option) {
  286. case 'S':
  287. if ((Options & SS_DAEMON_OPTION_STOP) != 0) {
  288. SwPrintError(0,
  289. NULL,
  290. "Exactly one of --start or --stop must be "
  291. "specified.");
  292. Status = 1;
  293. goto MainEnd;
  294. }
  295. Options |= SS_DAEMON_OPTION_START;
  296. break;
  297. case 'K':
  298. if ((Options & SS_DAEMON_OPTION_START) != 0) {
  299. SwPrintError(0,
  300. NULL,
  301. "Exactly one of --start or --stop must be "
  302. "specified.");
  303. Status = 1;
  304. goto MainEnd;
  305. }
  306. Options |= SS_DAEMON_OPTION_STOP;
  307. break;
  308. case 'p':
  309. Context.PidFilePath = optarg;
  310. break;
  311. case 'x':
  312. Context.ExecPath = optarg;
  313. break;
  314. case 'n':
  315. Context.ProcessName = optarg;
  316. break;
  317. case 'u':
  318. Status = SwGetUserIdFromName(optarg, &(Context.MatchUserId));
  319. if (Status != 0) {
  320. Context.MatchUserId = strtoul(optarg, &AfterScan, 10);
  321. if (AfterScan == optarg) {
  322. SwPrintError(0, optarg, "Invalid user");
  323. goto MainEnd;
  324. }
  325. }
  326. break;
  327. case 'g':
  328. Status = SwGetGroupIdFromName(optarg, &(Context.ChangeGroup));
  329. if (Status != 0) {
  330. Context.ChangeGroup = strtoul(optarg, &AfterScan, 10);
  331. if (AfterScan == optarg) {
  332. SwPrintError(0, optarg, "Invalid group");
  333. goto MainEnd;
  334. }
  335. }
  336. break;
  337. case 's':
  338. Context.Signal = SwGetSignalNumberFromName(optarg);
  339. if (Context.Signal == -1) {
  340. SwPrintError(0, optarg, "Invalid signal");
  341. Status = EINVAL;
  342. goto MainEnd;
  343. }
  344. break;
  345. case 'a':
  346. Context.StartAs = optarg;
  347. break;
  348. case 't':
  349. Options |= SS_DAEMON_OPTION_TEST;
  350. break;
  351. case 'o':
  352. Options |= SS_DAEMON_OPTION_NOTHING_OK;
  353. break;
  354. case 'q':
  355. Options |= SS_DAEMON_OPTION_QUIET;
  356. Options &= ~SS_DAEMON_OPTION_VERBOSE;
  357. break;
  358. case 'v':
  359. Options |= SS_DAEMON_OPTION_VERBOSE;
  360. Options &= ~SS_DAEMON_OPTION_QUIET;
  361. break;
  362. case 'c':
  363. Status = SwParseUserAndGroupString(optarg,
  364. &(Context.ChangeUser),
  365. &DummyGroup);
  366. if (Status != 0) {
  367. SwPrintError(0, optarg, "Invalid user:group");
  368. goto MainEnd;
  369. }
  370. if (DummyGroup != -1) {
  371. if (Context.ChangeGroup == -1) {
  372. Context.ChangeGroup = DummyGroup;
  373. }
  374. }
  375. break;
  376. case 'r':
  377. Context.RootDirectory = optarg;
  378. break;
  379. case 'd':
  380. Context.WorkingDirectory = optarg;
  381. break;
  382. case 'b':
  383. Options |= SS_DAEMON_OPTION_BACKGROUND;
  384. break;
  385. case 'C':
  386. Options |= SS_DAEMON_OPTION_NO_CLOSE;
  387. break;
  388. case 'N':
  389. Context.NiceLevel = strtoul(optarg, &AfterScan, 10);
  390. if (AfterScan == optarg) {
  391. SwPrintError(0, optarg, "Invalid nice level");
  392. Status = EINVAL;
  393. goto MainEnd;
  394. }
  395. Options |= SS_DAEMON_OPTION_NICE;
  396. break;
  397. case 'k':
  398. Context.Umask = strtoul(optarg, &AfterScan, 8);
  399. if (AfterScan == optarg) {
  400. SwPrintError(0, optarg, "Invalid umask");
  401. Status = EINVAL;
  402. goto MainEnd;
  403. }
  404. break;
  405. case 'm':
  406. Options |= SS_DAEMON_OPTION_MAKE_PIDFILE;
  407. break;
  408. case 'V':
  409. SwPrintVersion(SS_DAEMON_VERSION_MAJOR, SS_DAEMON_VERSION_MINOR);
  410. return 1;
  411. case 'H':
  412. printf(SS_DAEMON_USAGE);
  413. return 1;
  414. default:
  415. assert(FALSE);
  416. Status = 1;
  417. goto MainEnd;
  418. }
  419. }
  420. Context.Options = Options;
  421. ArgumentIndex = optind;
  422. if (ArgumentIndex > ArgumentCount) {
  423. ArgumentIndex = ArgumentCount;
  424. }
  425. //
  426. // At least one of start or stop is required.
  427. //
  428. if ((Options & (SS_DAEMON_OPTION_STOP | SS_DAEMON_OPTION_START)) == 0) {
  429. SwPrintError(0,
  430. NULL,
  431. "Exactly one of --start or --stop must be specified.");
  432. Status = 1;
  433. goto MainEnd;
  434. }
  435. //
  436. // A pidfile is required if the caller wants to make one.
  437. //
  438. if (((Options & SS_DAEMON_OPTION_MAKE_PIDFILE) != 0) &&
  439. (Context.PidFilePath == NULL)) {
  440. SwPrintError(0, NULL, "-p is required with -m");
  441. Status = EINVAL;
  442. goto MainEnd;
  443. }
  444. //
  445. // Check stop requirements.
  446. //
  447. if ((Options & SS_DAEMON_OPTION_STOP) != 0) {
  448. if ((Context.ExecPath == NULL) &&
  449. (Context.PidFilePath == NULL) &&
  450. (Context.MatchUserId == -1) &&
  451. (Context.ProcessName == NULL)) {
  452. SwPrintError(0, NULL, "At least one of -xpun is required with -K");
  453. Status = EINVAL;
  454. goto MainEnd;
  455. }
  456. } else if ((Options & SS_DAEMON_OPTION_START) != 0) {
  457. if ((Context.ExecPath == NULL) && (Context.StartAs == NULL)) {
  458. SwPrintError(0, NULL, "At least one of -xa is required for -S");
  459. Status = EINVAL;
  460. goto MainEnd;
  461. }
  462. //
  463. // At least one of start or stop is required.
  464. //
  465. } else {
  466. SwPrintError(0,
  467. NULL,
  468. "Exactly one of --start or --stop must be specified.");
  469. Status = EINVAL;
  470. goto MainEnd;
  471. }
  472. //
  473. // If no exec name was given but a start-as was, use that as the exec name.
  474. //
  475. if (Context.StartAs == NULL) {
  476. Context.StartAs = Context.ExecPath;
  477. }
  478. if ((Context.ExecPath == NULL) && (Context.StartAs != NULL)) {
  479. Context.ExecPath = Context.StartAs;
  480. }
  481. //
  482. // Chroot if requested.
  483. //
  484. if (Context.RootDirectory != NULL) {
  485. Status = chroot(Context.RootDirectory);
  486. if (Status != 0) {
  487. Status = errno;
  488. SwPrintError(Status, Context.RootDirectory, "Failed to chroot");
  489. goto MainEnd;
  490. }
  491. Status = chdir("/");
  492. if (Status != 0) {
  493. Status = errno;
  494. SwPrintError(Status, Context.RootDirectory, "Failed to chdir");
  495. goto MainEnd;
  496. }
  497. }
  498. //
  499. // Change directories if requested too.
  500. //
  501. if (Context.WorkingDirectory != NULL) {
  502. Status = chdir(Context.WorkingDirectory);
  503. if (Status != 0) {
  504. Status = errno;
  505. SwPrintError(Status,
  506. Context.WorkingDirectory,
  507. "Failed to change directory");
  508. goto MainEnd;
  509. }
  510. }
  511. //
  512. // Open up the pid file if specified.
  513. //
  514. if (Context.PidFilePath != NULL) {
  515. Status = SsDaemonReadPidFile(&Context);
  516. if (Status != 0) {
  517. SwPrintError(0, Context.PidFilePath, "Failed to read");
  518. goto MainEnd;
  519. }
  520. } else {
  521. Status = SsDaemonMatchAllProcesses(&Context);
  522. if (Status != 0) {
  523. SwPrintError(0, NULL, "Failed to get process list");
  524. }
  525. }
  526. //
  527. // Perform stop actions if this is a stop request.
  528. //
  529. if ((Options & SS_DAEMON_OPTION_STOP) != 0) {
  530. ActionCount = SsDaemonStop(&Context);
  531. if (ActionCount == 0) {
  532. if ((Options & SS_DAEMON_OPTION_NOTHING_OK) != 0) {
  533. Status = 0;
  534. } else {
  535. Status = 1;
  536. }
  537. } else {
  538. Status = 0;
  539. }
  540. goto MainEnd;
  541. }
  542. //
  543. // This is a start operation. If something matches already, do nothing.
  544. //
  545. if (Context.MatchProcessIdCount != 0) {
  546. if ((Options & SS_DAEMON_OPTION_QUIET) == 0) {
  547. printf("%s is already running with pid %lu\n",
  548. Context.ExecPath,
  549. Context.MatchProcessIds[0]);
  550. }
  551. Status = 1;
  552. if ((Options & SS_DAEMON_OPTION_NOTHING_OK) != 0) {
  553. Status = 0;
  554. }
  555. goto MainEnd;
  556. }
  557. //
  558. // Create the new arguments array.
  559. //
  560. assert(ArgumentIndex != 0);
  561. ArgumentIndex -= 1;
  562. NewArguments = malloc(sizeof(PSTR) * (ArgumentCount - ArgumentIndex + 1));
  563. if (NewArguments == NULL) {
  564. Status = ENOMEM;
  565. goto MainEnd;
  566. }
  567. memcpy(NewArguments,
  568. &(Arguments[ArgumentIndex]),
  569. sizeof(PSTR) * (ArgumentCount - ArgumentIndex + 1));
  570. NewArguments[0] = Context.StartAs;
  571. //
  572. // Get into the background if requested.
  573. //
  574. if ((Options & SS_DAEMON_OPTION_BACKGROUND) != 0) {
  575. //
  576. // Fork and exit in the parent, continue in the child.
  577. //
  578. Child = fork();
  579. if (Child < 0) {
  580. Status = errno;
  581. SwPrintError(Status, NULL, "Failed to fork");
  582. goto MainEnd;
  583. }
  584. //
  585. // Exit immediately in the parent, continue in the child.
  586. //
  587. if (Child > 0) {
  588. Status = 0;
  589. goto MainEnd;
  590. }
  591. //
  592. // Become a session leader, detaching from the controlling terminal.
  593. //
  594. setsid();
  595. //
  596. // Point standard in, out, and error at /dev/null.
  597. //
  598. if ((Options & SS_DAEMON_OPTION_NO_CLOSE) == 0) {
  599. DevNull = open("/dev/null", O_RDWR);
  600. if (DevNull >= 0) {
  601. dup2(DevNull, STDIN_FILENO);
  602. dup2(DevNull, STDOUT_FILENO);
  603. dup2(DevNull, STDERR_FILENO);
  604. close(DevNull);
  605. }
  606. SwCloseFrom(STDERR_FILENO + 1);
  607. }
  608. //
  609. // Double fork. Do this to prevent the grandchild process from ever
  610. // acquiring a controlling terminal. Since only the session leader
  611. // can acquire a controlling terminal, after the fork its new PID
  612. // will not be the session leader ID.
  613. //
  614. Child = fork();
  615. if (Child < 0) {
  616. exit(1);
  617. } else if (Child != 0) {
  618. exit(0);
  619. }
  620. //
  621. // The remainder now runs as the grandchild.
  622. //
  623. }
  624. //
  625. // Write the pid file.
  626. //
  627. if ((Options & SS_DAEMON_OPTION_MAKE_PIDFILE) != 0) {
  628. SsDaemonWritePidFile(&Context);
  629. }
  630. //
  631. // Change identity if requested.
  632. //
  633. if (Context.ChangeUser != -1) {
  634. User = getpwuid(Context.ChangeUser);
  635. if (Context.ChangeGroup != -1) {
  636. User->pw_gid = Context.ChangeGroup;
  637. }
  638. SwBecomeUser(User);
  639. } else if (Context.ChangeGroup != -1) {
  640. Status = setgid(Context.ChangeGroup);
  641. if (Status != 0) {
  642. Status = errno;
  643. SwPrintError(Status, NULL, "setgid failed");
  644. goto MainEnd;
  645. }
  646. setgroups(1, &(Context.ChangeGroup));
  647. }
  648. //
  649. // Change nice level if requested.
  650. //
  651. if ((Options & SS_DAEMON_OPTION_NICE) != 0) {
  652. Status = nice(Context.NiceLevel);
  653. if (Status != 0) {
  654. Status = errno;
  655. SwPrintError(Status, NULL, "nice failed");
  656. goto MainEnd;
  657. }
  658. }
  659. //
  660. // Change the umask if requested.
  661. //
  662. if (Context.Umask != -1) {
  663. umask(Context.Umask);
  664. }
  665. //
  666. // Make it rain.
  667. //
  668. execvp(NewArguments[0], NewArguments);
  669. Status = errno;
  670. SwPrintError(Status, Arguments[ArgumentIndex], "Cannot execute");
  671. MainEnd:
  672. if (NewArguments != NULL) {
  673. free(NewArguments);
  674. }
  675. if (Context.MatchProcessIds != NULL) {
  676. free(Context.MatchProcessIds);
  677. }
  678. return Status;
  679. }
  680. //
  681. // --------------------------------------------------------- Internal Functions
  682. //
  683. INT
  684. SsDaemonReadPidFile (
  685. PSS_DAEMON_CONTEXT Context
  686. )
  687. /*++
  688. Routine Description:
  689. This routine tries to read a pid file. It is not an error if the file does
  690. not exist.
  691. Arguments:
  692. Context - Supplies a pointer to the application context.
  693. Return Value:
  694. 0 on success or if the file does not exist.
  695. Non-zero on any other failures.
  696. --*/
  697. {
  698. FILE *File;
  699. unsigned int ScannedPid;
  700. File = fopen(Context->PidFilePath, "r");
  701. if (File == NULL) {
  702. if (errno == ENOENT) {
  703. return 0;
  704. }
  705. SwPrintError(errno, Context->PidFilePath, "Cannot open");
  706. return errno;
  707. }
  708. if (fscanf(File, "%u", &ScannedPid) != 1) {
  709. return EINVAL;
  710. }
  711. SsDaemonMatchPid(Context, ScannedPid);
  712. return 0;
  713. }
  714. VOID
  715. SsDaemonWritePidFile (
  716. PSS_DAEMON_CONTEXT Context
  717. )
  718. /*++
  719. Routine Description:
  720. This routine tries to write the current process ID to a pid file.
  721. Arguments:
  722. Context - Supplies a pointer to the application context.
  723. Return Value:
  724. None. At this point it's too late for errors.
  725. --*/
  726. {
  727. CHAR Buffer[64];
  728. ssize_t BytesWritten;
  729. int File;
  730. int Size;
  731. size_t TotalBytesWritten;
  732. File = open(Context->PidFilePath, O_WRONLY | O_TRUNC | O_CREAT, 0644);
  733. if (File >= 0) {
  734. Size = snprintf(Buffer,
  735. sizeof(Buffer),
  736. "%lu",
  737. (long unsigned int)getpid());
  738. TotalBytesWritten = 0;
  739. do {
  740. BytesWritten = write(File,
  741. Buffer + TotalBytesWritten,
  742. Size - TotalBytesWritten);
  743. if (BytesWritten <= 0) {
  744. if (errno == EINTR) {
  745. continue;
  746. }
  747. break;
  748. }
  749. TotalBytesWritten += BytesWritten;
  750. } while (TotalBytesWritten < Size);
  751. close(File);
  752. }
  753. return;
  754. }
  755. INT
  756. SsDaemonMatchAllProcesses (
  757. PSS_DAEMON_CONTEXT Context
  758. )
  759. /*++
  760. Routine Description:
  761. This routine reads in all the current processes.
  762. Arguments:
  763. Context - Supplies a pointer to the application context.
  764. Return Value:
  765. 0 on success.
  766. Non-zero on failure.
  767. --*/
  768. {
  769. size_t Count;
  770. UINTN Index;
  771. pid_t *ProcessIds;
  772. size_t Size;
  773. INT Status;
  774. ProcessIds = NULL;
  775. Size = 0;
  776. SwGetProcessIdList(NULL, &Size);
  777. if (Size == 0) {
  778. return ESRCH;
  779. }
  780. //
  781. // Add a fudge factor in case more processes come in.
  782. //
  783. Size *= 2;
  784. ProcessIds = malloc(Size);
  785. if (ProcessIds == NULL) {
  786. return ENOMEM;
  787. }
  788. memset(ProcessIds, 0, Size);
  789. Status = SwGetProcessIdList(ProcessIds, &Size);
  790. if (Status != 0) {
  791. SwPrintError(0, NULL, "Failed to get process ID list");
  792. goto GetAllProcessesEnd;
  793. }
  794. Count = Size / sizeof(pid_t);
  795. for (Index = 0; Index < Count; Index += 1) {
  796. SsDaemonMatchPid(Context, ProcessIds[Index]);
  797. }
  798. Status = 0;
  799. GetAllProcessesEnd:
  800. if (ProcessIds != NULL) {
  801. free(ProcessIds);
  802. }
  803. return Status;
  804. }
  805. VOID
  806. SsDaemonMatchPid (
  807. PSS_DAEMON_CONTEXT Context,
  808. pid_t ProcessId
  809. )
  810. /*++
  811. Routine Description:
  812. This routine matches a given process ID against the user specified criteria.
  813. If the given process ID matches, it will be added to the context's array
  814. of matching processes.
  815. Arguments:
  816. Context - Supplies a pointer to the application context.
  817. ProcessId - Supplies the process ID to match against.
  818. Return Value:
  819. None.
  820. --*/
  821. {
  822. PSTR BaseName;
  823. BOOL Match;
  824. PVOID NewBuffer;
  825. UINTN NewCapacity;
  826. PSWISS_PROCESS_INFORMATION ProcessInformation;
  827. int Status;
  828. Status = SwGetProcessInformation(ProcessId, &ProcessInformation);
  829. if (Status != 0) {
  830. return;
  831. }
  832. Match = TRUE;
  833. if (Context->ExecPath != NULL) {
  834. if (strcmp(ProcessInformation->Name, Context->ExecPath) != 0) {
  835. Match = FALSE;
  836. }
  837. }
  838. if ((Match != FALSE) && (Context->ProcessName != NULL)) {
  839. BaseName = basename(ProcessInformation->Name);
  840. if (strcmp(BaseName, Context->ProcessName) != 0) {
  841. Match = FALSE;
  842. }
  843. }
  844. if ((Match != FALSE) && (Context->MatchUserId != -1)) {
  845. if (ProcessInformation->RealUserId != Context->MatchUserId) {
  846. Match = FALSE;
  847. }
  848. }
  849. SwDestroyProcessInformation(ProcessInformation);
  850. if (Match == FALSE) {
  851. return;
  852. }
  853. //
  854. // Add the process to the match array.
  855. //
  856. if (Context->MatchProcessIdCount >= Context->MatchProcessIdCapacity) {
  857. if (Context->MatchProcessIdCount == 0) {
  858. NewCapacity = SS_DAEMON_INITIAL_MATCH_ARRAY_SIZE;
  859. } else {
  860. NewCapacity = Context->MatchProcessIdCapacity * 2;
  861. }
  862. assert(NewCapacity > Context->MatchProcessIdCount + 1);
  863. NewBuffer = realloc(Context->MatchProcessIds,
  864. NewCapacity * sizeof(pid_t));
  865. if (NewBuffer == NULL) {
  866. //
  867. // Quietly ignore allocation failures.
  868. //
  869. return;
  870. }
  871. Context->MatchProcessIds = NewBuffer;
  872. Context->MatchProcessIdCapacity = NewCapacity;
  873. }
  874. Context->MatchProcessIds[Context->MatchProcessIdCount] = ProcessId;
  875. Context->MatchProcessIdCount += 1;
  876. return;
  877. }
  878. INT
  879. SsDaemonStop (
  880. PSS_DAEMON_CONTEXT Context
  881. )
  882. /*++
  883. Routine Description:
  884. This routine performs stop actions on matchind PIDs.
  885. Arguments:
  886. Context - Supplies a pointer to the application context.
  887. Return Value:
  888. Returns the number of actions performed.
  889. --*/
  890. {
  891. UINTN Index;
  892. PSTR Plural;
  893. INT ProcessesKilled;
  894. INT Signal;
  895. ProcessesKilled = 0;
  896. if (Context->MatchProcessIdCount == 0) {
  897. SsDaemonPrintStopDescription(Context);
  898. printf(": No processes found\n");
  899. goto StopEnd;
  900. }
  901. for (Index = 0; Index < Context->MatchProcessIdCount; Index += 1) {
  902. Signal = Context->Signal;
  903. if ((Context->Options & SS_DAEMON_OPTION_TEST) != 0) {
  904. Signal = 0;
  905. }
  906. if (kill(Context->MatchProcessIds[Index], Signal) == 0) {
  907. ProcessesKilled += 1;
  908. } else {
  909. SwPrintError(errno,
  910. NULL,
  911. "Failed to kill process %lu",
  912. (long unsigned int)(Context->MatchProcessIds[Index]));
  913. Context->MatchProcessIds[Index] = 0;
  914. if ((Context->Options & SS_DAEMON_OPTION_TEST) != 0) {
  915. ProcessesKilled = -1;
  916. goto StopEnd;
  917. }
  918. }
  919. }
  920. if ((ProcessesKilled > 0) &&
  921. ((Context->Options & SS_DAEMON_OPTION_QUIET) == 0)) {
  922. printf("Stopped ");
  923. SsDaemonPrintStopDescription(Context);
  924. Plural = "";
  925. if (ProcessesKilled > 1) {
  926. Plural = "s";
  927. }
  928. printf(" (pid%s", Plural);
  929. for (Index = 0; Index < Context->MatchProcessIdCount; Index += 1) {
  930. if (Context->MatchProcessIds[Index] != 0) {
  931. printf(" %lu",
  932. (long unsigned int)(Context->MatchProcessIds[Index]));
  933. }
  934. }
  935. printf(")\n");
  936. }
  937. StopEnd:
  938. return ProcessesKilled;
  939. }
  940. VOID
  941. SsDaemonPrintStopDescription (
  942. PSS_DAEMON_CONTEXT Context
  943. )
  944. /*++
  945. Routine Description:
  946. This routine prints to standard out a description of what is being stopped,
  947. without a newline.
  948. Arguments:
  949. Context - Supplies a pointer to the application context.
  950. Return Value:
  951. None.
  952. --*/
  953. {
  954. PSTR Space;
  955. Space = "";
  956. if (Context->ProcessName != NULL) {
  957. printf("%s", Context->ProcessName);
  958. Space = " ";
  959. }
  960. if (Context->ExecPath != NULL) {
  961. printf("%s%s", Space, Context->ExecPath);
  962. Space = " ";
  963. }
  964. if (Context->PidFilePath != NULL) {
  965. printf("%sprocess in pid file '%s'", Space, Context->PidFilePath);
  966. Space = " ";
  967. }
  968. if (Context->MatchUserId != -1) {
  969. printf("%sprocesses owned by user %ld",
  970. Space,
  971. (long int)Context->MatchUserId);
  972. }
  973. return;
  974. }