crond.c 20 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * crond -d[#] -c <crondir> -f -b
  4. *
  5. * run as root, but NOT setuid root
  6. *
  7. * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
  8. * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
  9. *
  10. * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  11. */
  12. #define VERSION "2.3.2"
  13. #include "busybox.h"
  14. #include <sys/syslog.h>
  15. #define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
  16. #ifndef CRONTABS
  17. #define CRONTABS "/var/spool/cron/crontabs"
  18. #endif
  19. #ifndef TMPDIR
  20. #define TMPDIR "/var/spool/cron"
  21. #endif
  22. #ifndef SENDMAIL
  23. #define SENDMAIL "/usr/sbin/sendmail"
  24. #endif
  25. #ifndef SENDMAIL_ARGS
  26. #define SENDMAIL_ARGS "-ti", "oem"
  27. #endif
  28. #ifndef CRONUPDATE
  29. #define CRONUPDATE "cron.update"
  30. #endif
  31. #ifndef MAXLINES
  32. #define MAXLINES 256 /* max lines in non-root crontabs */
  33. #endif
  34. typedef struct CronFile {
  35. struct CronFile *cf_Next;
  36. struct CronLine *cf_LineBase;
  37. char *cf_User; /* username */
  38. int cf_Ready; /* bool: one or more jobs ready */
  39. int cf_Running; /* bool: one or more jobs running */
  40. int cf_Deleted; /* marked for deletion, ignore */
  41. } CronFile;
  42. typedef struct CronLine {
  43. struct CronLine *cl_Next;
  44. char *cl_Shell; /* shell command */
  45. pid_t cl_Pid; /* running pid, 0, or armed (-1) */
  46. int cl_MailFlag; /* running pid is for mail */
  47. int cl_MailPos; /* 'empty file' size */
  48. char cl_Mins[60]; /* 0-59 */
  49. char cl_Hrs[24]; /* 0-23 */
  50. char cl_Days[32]; /* 1-31 */
  51. char cl_Mons[12]; /* 0-11 */
  52. char cl_Dow[7]; /* 0-6, beginning sunday */
  53. } CronLine;
  54. #define RUN_RANOUT 1
  55. #define RUN_RUNNING 2
  56. #define RUN_FAILED 3
  57. #define DaemonUid 0
  58. #if ENABLE_DEBUG_CROND_OPTION
  59. static unsigned DebugOpt;
  60. #endif
  61. static unsigned LogLevel = 8;
  62. static const char *LogFile;
  63. static const char *CDir = CRONTABS;
  64. static void startlogger(void);
  65. static void CheckUpdates(void);
  66. static void SynchronizeDir(void);
  67. static int TestJobs(time_t t1, time_t t2);
  68. static void RunJobs(void);
  69. static int CheckJobs(void);
  70. static void RunJob(const char *user, CronLine * line);
  71. #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
  72. static void EndJob(const char *user, CronLine * line);
  73. #else
  74. #define EndJob(user, line) line->cl_Pid = 0
  75. #endif
  76. static void DeleteFile(const char *userName);
  77. static CronFile *FileBase;
  78. static void crondlog(const char *ctl, ...)
  79. {
  80. va_list va;
  81. const char *fmt;
  82. int level = (int) (ctl[0] & 0xf);
  83. int type = level == 20 ?
  84. LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
  85. va_start(va, ctl);
  86. fmt = ctl + 1;
  87. if (level >= LogLevel) {
  88. #if ENABLE_DEBUG_CROND_OPTION
  89. if (DebugOpt) {
  90. vfprintf(stderr, fmt, va);
  91. } else
  92. #endif
  93. if (LogFile == 0) {
  94. vsyslog(type, fmt, va);
  95. } else {
  96. int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
  97. if (logfd >= 0) {
  98. vdprintf(logfd, fmt, va);
  99. close(logfd);
  100. #if ENABLE_DEBUG_CROND_OPTION
  101. } else {
  102. bb_perror_msg("can't open log file");
  103. #endif
  104. }
  105. }
  106. }
  107. va_end(va);
  108. if (ctl[0] & 0200) {
  109. exit(20);
  110. }
  111. }
  112. int crond_main(int ac, char **av)
  113. {
  114. unsigned opt;
  115. char *lopt, *Lopt, *copt;
  116. #if ENABLE_DEBUG_CROND_OPTION
  117. char *dopt;
  118. opt_complementary = "f-b:b-f:S-L:L-S:d-l";
  119. #else
  120. opt_complementary = "f-b:b-f:S-L:L-S";
  121. #endif
  122. opterr = 0; /* disable getopt 'errors' message. */
  123. opt = getopt32(ac, av, "l:L:fbSc:"
  124. #if ENABLE_DEBUG_CROND_OPTION
  125. "d:"
  126. #endif
  127. , &lopt, &Lopt, &copt
  128. #if ENABLE_DEBUG_CROND_OPTION
  129. , &dopt
  130. #endif
  131. );
  132. if (opt & 1) {
  133. LogLevel = xatou(lopt);
  134. }
  135. if (opt & 2) {
  136. if (*Lopt != 0) {
  137. LogFile = Lopt;
  138. }
  139. }
  140. if (opt & 32) {
  141. if (*copt != 0) {
  142. CDir = copt;
  143. }
  144. }
  145. #if ENABLE_DEBUG_CROND_OPTION
  146. if (opt & 64) {
  147. DebugOpt = xatou(dopt);
  148. LogLevel = 0;
  149. }
  150. #endif
  151. /*
  152. * change directory
  153. */
  154. xchdir(CDir);
  155. signal(SIGHUP, SIG_IGN); /* hmm.. but, if kill -HUP original
  156. * version - his died. ;(
  157. */
  158. /*
  159. * close stdin and stdout, stderr.
  160. * close unused descriptors - don't need.
  161. * optional detach from controlling terminal
  162. */
  163. if (!(opt & 4)) {
  164. #ifdef BB_NOMMU
  165. /* reexec for vfork() do continue parent */
  166. vfork_daemon_rexec(1, 0, ac, av, "-f");
  167. #else
  168. xdaemon(1, 0);
  169. #endif
  170. }
  171. (void) startlogger(); /* need if syslog mode selected */
  172. /*
  173. * main loop - synchronize to 1 second after the minute, minimum sleep
  174. * of 1 second.
  175. */
  176. crondlog("\011%s " VERSION " dillon, started, log level %d\n",
  177. applet_name, LogLevel);
  178. SynchronizeDir();
  179. {
  180. time_t t1 = time(NULL);
  181. time_t t2;
  182. long dt;
  183. int rescan = 60;
  184. short sleep_time = 60;
  185. for (;;) {
  186. sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time));
  187. t2 = time(NULL);
  188. dt = t2 - t1;
  189. /*
  190. * The file 'cron.update' is checked to determine new cron
  191. * jobs. The directory is rescanned once an hour to deal
  192. * with any screwups.
  193. *
  194. * check for disparity. Disparities over an hour either way
  195. * result in resynchronization. A reverse-indexed disparity
  196. * less then an hour causes us to effectively sleep until we
  197. * match the original time (i.e. no re-execution of jobs that
  198. * have just been run). A forward-indexed disparity less then
  199. * an hour causes intermediate jobs to be run, but only once
  200. * in the worst case.
  201. *
  202. * when running jobs, the inequality used is greater but not
  203. * equal to t1, and less then or equal to t2.
  204. */
  205. if (--rescan == 0) {
  206. rescan = 60;
  207. SynchronizeDir();
  208. }
  209. CheckUpdates();
  210. #if ENABLE_DEBUG_CROND_OPTION
  211. if (DebugOpt)
  212. crondlog("\005Wakeup dt=%d\n", dt);
  213. #endif
  214. if (dt < -60 * 60 || dt > 60 * 60) {
  215. t1 = t2;
  216. crondlog("\111time disparity of %d minutes detected\n", dt / 60);
  217. } else if (dt > 0) {
  218. TestJobs(t1, t2);
  219. RunJobs();
  220. sleep(5);
  221. if (CheckJobs() > 0) {
  222. sleep_time = 10;
  223. } else {
  224. sleep_time = 60;
  225. }
  226. t1 = t2;
  227. }
  228. }
  229. }
  230. return 0; /* not reached */
  231. }
  232. static int ChangeUser(const char *user)
  233. {
  234. struct passwd *pas;
  235. const char *err_msg;
  236. /*
  237. * Obtain password entry and change privileges
  238. */
  239. pas = getpwnam(user);
  240. if (pas == 0) {
  241. crondlog("\011failed to get uid for %s", user);
  242. return -1;
  243. }
  244. setenv("USER", pas->pw_name, 1);
  245. setenv("HOME", pas->pw_dir, 1);
  246. setenv("SHELL", DEFAULT_SHELL, 1);
  247. /*
  248. * Change running state to the user in question
  249. */
  250. err_msg = change_identity_e2str(pas);
  251. if (err_msg) {
  252. crondlog("\011%s for user %s", err_msg, user);
  253. return -1;
  254. }
  255. if (chdir(pas->pw_dir) < 0) {
  256. crondlog("\011chdir failed: %s: %m", pas->pw_dir);
  257. if (chdir(TMPDIR) < 0) {
  258. crondlog("\011chdir failed: %s: %m", TMPDIR);
  259. return -1;
  260. }
  261. }
  262. return pas->pw_uid;
  263. }
  264. static void startlogger(void)
  265. {
  266. if (LogFile == 0) {
  267. openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
  268. }
  269. #if ENABLE_DEBUG_CROND_OPTION
  270. else { /* test logfile */
  271. int logfd;
  272. if ((logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600)) >= 0) {
  273. close(logfd);
  274. } else {
  275. bb_perror_msg("failed to open log file '%s': ", LogFile);
  276. }
  277. }
  278. #endif
  279. }
  280. static const char *const DowAry[] = {
  281. "sun",
  282. "mon",
  283. "tue",
  284. "wed",
  285. "thu",
  286. "fri",
  287. "sat",
  288. "Sun",
  289. "Mon",
  290. "Tue",
  291. "Wed",
  292. "Thu",
  293. "Fri",
  294. "Sat",
  295. NULL
  296. };
  297. static const char *const MonAry[] = {
  298. "jan",
  299. "feb",
  300. "mar",
  301. "apr",
  302. "may",
  303. "jun",
  304. "jul",
  305. "aug",
  306. "sep",
  307. "oct",
  308. "nov",
  309. "dec",
  310. "Jan",
  311. "Feb",
  312. "Mar",
  313. "Apr",
  314. "May",
  315. "Jun",
  316. "Jul",
  317. "Aug",
  318. "Sep",
  319. "Oct",
  320. "Nov",
  321. "Dec",
  322. NULL
  323. };
  324. static char *ParseField(char *user, char *ary, int modvalue, int off,
  325. const char *const *names, char *ptr)
  326. {
  327. char *base = ptr;
  328. int n1 = -1;
  329. int n2 = -1;
  330. if (base == NULL) {
  331. return NULL;
  332. }
  333. while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
  334. int skip = 0;
  335. /* Handle numeric digit or symbol or '*' */
  336. if (*ptr == '*') {
  337. n1 = 0; /* everything will be filled */
  338. n2 = modvalue - 1;
  339. skip = 1;
  340. ++ptr;
  341. } else if (*ptr >= '0' && *ptr <= '9') {
  342. if (n1 < 0) {
  343. n1 = strtol(ptr, &ptr, 10) + off;
  344. } else {
  345. n2 = strtol(ptr, &ptr, 10) + off;
  346. }
  347. skip = 1;
  348. } else if (names) {
  349. int i;
  350. for (i = 0; names[i]; ++i) {
  351. if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
  352. break;
  353. }
  354. }
  355. if (names[i]) {
  356. ptr += strlen(names[i]);
  357. if (n1 < 0) {
  358. n1 = i;
  359. } else {
  360. n2 = i;
  361. }
  362. skip = 1;
  363. }
  364. }
  365. /* handle optional range '-' */
  366. if (skip == 0) {
  367. crondlog("\111failed user %s parsing %s\n", user, base);
  368. return NULL;
  369. }
  370. if (*ptr == '-' && n2 < 0) {
  371. ++ptr;
  372. continue;
  373. }
  374. /*
  375. * collapse single-value ranges, handle skipmark, and fill
  376. * in the character array appropriately.
  377. */
  378. if (n2 < 0) {
  379. n2 = n1;
  380. }
  381. if (*ptr == '/') {
  382. skip = strtol(ptr + 1, &ptr, 10);
  383. }
  384. /*
  385. * fill array, using a failsafe is the easiest way to prevent
  386. * an endless loop
  387. */
  388. {
  389. int s0 = 1;
  390. int failsafe = 1024;
  391. --n1;
  392. do {
  393. n1 = (n1 + 1) % modvalue;
  394. if (--s0 == 0) {
  395. ary[n1 % modvalue] = 1;
  396. s0 = skip;
  397. }
  398. }
  399. while (n1 != n2 && --failsafe);
  400. if (failsafe == 0) {
  401. crondlog("\111failed user %s parsing %s\n", user, base);
  402. return NULL;
  403. }
  404. }
  405. if (*ptr != ',') {
  406. break;
  407. }
  408. ++ptr;
  409. n1 = -1;
  410. n2 = -1;
  411. }
  412. if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
  413. crondlog("\111failed user %s parsing %s\n", user, base);
  414. return NULL;
  415. }
  416. while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') {
  417. ++ptr;
  418. }
  419. #if ENABLE_DEBUG_CROND_OPTION
  420. if (DebugOpt) {
  421. int i;
  422. for (i = 0; i < modvalue; ++i) {
  423. crondlog("\005%d", ary[i]);
  424. }
  425. crondlog("\005\n");
  426. }
  427. #endif
  428. return ptr;
  429. }
  430. static void FixDayDow(CronLine * line)
  431. {
  432. int i;
  433. int weekUsed = 0;
  434. int daysUsed = 0;
  435. for (i = 0; i < (int)(arysize(line->cl_Dow)); ++i) {
  436. if (line->cl_Dow[i] == 0) {
  437. weekUsed = 1;
  438. break;
  439. }
  440. }
  441. for (i = 0; i < (int)(arysize(line->cl_Days)); ++i) {
  442. if (line->cl_Days[i] == 0) {
  443. daysUsed = 1;
  444. break;
  445. }
  446. }
  447. if (weekUsed && !daysUsed) {
  448. memset(line->cl_Days, 0, sizeof(line->cl_Days));
  449. }
  450. if (daysUsed && !weekUsed) {
  451. memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
  452. }
  453. }
  454. static void SynchronizeFile(const char *fileName)
  455. {
  456. int maxEntries = MAXLINES;
  457. int maxLines;
  458. char buf[1024];
  459. if (strcmp(fileName, "root") == 0) {
  460. maxEntries = 65535;
  461. }
  462. maxLines = maxEntries * 10;
  463. if (fileName) {
  464. FILE *fi;
  465. DeleteFile(fileName);
  466. fi = fopen(fileName, "r");
  467. if (fi != NULL) {
  468. struct stat sbuf;
  469. if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
  470. CronFile *file = calloc(1, sizeof(CronFile));
  471. CronLine **pline;
  472. file->cf_User = strdup(fileName);
  473. pline = &file->cf_LineBase;
  474. while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
  475. CronLine line;
  476. char *ptr;
  477. trim(buf);
  478. if (buf[0] == 0 || buf[0] == '#') {
  479. continue;
  480. }
  481. if (--maxEntries == 0) {
  482. break;
  483. }
  484. memset(&line, 0, sizeof(line));
  485. #if ENABLE_DEBUG_CROND_OPTION
  486. if (DebugOpt) {
  487. crondlog("\111User %s Entry %s\n", fileName, buf);
  488. }
  489. #endif
  490. /* parse date ranges */
  491. ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
  492. ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
  493. ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
  494. ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
  495. ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
  496. /* check failure */
  497. if (ptr == NULL) {
  498. continue;
  499. }
  500. /*
  501. * fix days and dow - if one is not * and the other
  502. * is *, the other is set to 0, and vise-versa
  503. */
  504. FixDayDow(&line);
  505. *pline = calloc(1, sizeof(CronLine));
  506. **pline = line;
  507. /* copy command */
  508. (*pline)->cl_Shell = strdup(ptr);
  509. #if ENABLE_DEBUG_CROND_OPTION
  510. if (DebugOpt) {
  511. crondlog("\111 Command %s\n", ptr);
  512. }
  513. #endif
  514. pline = &((*pline)->cl_Next);
  515. }
  516. *pline = NULL;
  517. file->cf_Next = FileBase;
  518. FileBase = file;
  519. if (maxLines == 0 || maxEntries == 0) {
  520. crondlog("\111Maximum number of lines reached for user %s\n", fileName);
  521. }
  522. }
  523. fclose(fi);
  524. }
  525. }
  526. }
  527. static void CheckUpdates(void)
  528. {
  529. FILE *fi;
  530. char buf[256];
  531. fi = fopen(CRONUPDATE, "r");
  532. if (fi != NULL) {
  533. remove(CRONUPDATE);
  534. while (fgets(buf, sizeof(buf), fi) != NULL) {
  535. SynchronizeFile(strtok(buf, " \t\r\n"));
  536. }
  537. fclose(fi);
  538. }
  539. }
  540. static void SynchronizeDir(void)
  541. {
  542. /* Attempt to delete the database. */
  543. for (;;) {
  544. CronFile *file;
  545. for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next);
  546. if (file == NULL) {
  547. break;
  548. }
  549. DeleteFile(file->cf_User);
  550. }
  551. /*
  552. * Remove cron update file
  553. *
  554. * Re-chdir, in case directory was renamed & deleted, or otherwise
  555. * screwed up.
  556. *
  557. * scan directory and add associated users
  558. */
  559. remove(CRONUPDATE);
  560. if (chdir(CDir) < 0) {
  561. crondlog("\311cannot find %s\n", CDir);
  562. }
  563. {
  564. DIR *dir = opendir(".");
  565. struct dirent *den;
  566. if (dir) {
  567. while ((den = readdir(dir))) {
  568. if (strchr(den->d_name, '.') != NULL) {
  569. continue;
  570. }
  571. if (getpwnam(den->d_name)) {
  572. SynchronizeFile(den->d_name);
  573. } else {
  574. crondlog("\007ignoring %s\n", den->d_name);
  575. }
  576. }
  577. closedir(dir);
  578. } else {
  579. crondlog("\311cannot open current dir!\n");
  580. }
  581. }
  582. }
  583. /*
  584. * DeleteFile() - delete user database
  585. *
  586. * Note: multiple entries for same user may exist if we were unable to
  587. * completely delete a database due to running processes.
  588. */
  589. static void DeleteFile(const char *userName)
  590. {
  591. CronFile **pfile = &FileBase;
  592. CronFile *file;
  593. while ((file = *pfile) != NULL) {
  594. if (strcmp(userName, file->cf_User) == 0) {
  595. CronLine **pline = &file->cf_LineBase;
  596. CronLine *line;
  597. file->cf_Running = 0;
  598. file->cf_Deleted = 1;
  599. while ((line = *pline) != NULL) {
  600. if (line->cl_Pid > 0) {
  601. file->cf_Running = 1;
  602. pline = &line->cl_Next;
  603. } else {
  604. *pline = line->cl_Next;
  605. free(line->cl_Shell);
  606. free(line);
  607. }
  608. }
  609. if (file->cf_Running == 0) {
  610. *pfile = file->cf_Next;
  611. free(file->cf_User);
  612. free(file);
  613. } else {
  614. pfile = &file->cf_Next;
  615. }
  616. } else {
  617. pfile = &file->cf_Next;
  618. }
  619. }
  620. }
  621. /*
  622. * TestJobs()
  623. *
  624. * determine which jobs need to be run. Under normal conditions, the
  625. * period is about a minute (one scan). Worst case it will be one
  626. * hour (60 scans).
  627. */
  628. static int TestJobs(time_t t1, time_t t2)
  629. {
  630. int nJobs = 0;
  631. time_t t;
  632. /* Find jobs > t1 and <= t2 */
  633. for (t = t1 - t1 % 60; t <= t2; t += 60) {
  634. if (t > t1) {
  635. struct tm *tp = localtime(&t);
  636. CronFile *file;
  637. CronLine *line;
  638. for (file = FileBase; file; file = file->cf_Next) {
  639. #if ENABLE_DEBUG_CROND_OPTION
  640. if (DebugOpt)
  641. crondlog("\005FILE %s:\n", file->cf_User);
  642. #endif
  643. if (file->cf_Deleted)
  644. continue;
  645. for (line = file->cf_LineBase; line; line = line->cl_Next) {
  646. #if ENABLE_DEBUG_CROND_OPTION
  647. if (DebugOpt)
  648. crondlog("\005 LINE %s\n", line->cl_Shell);
  649. #endif
  650. if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] &&
  651. (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
  652. && line->cl_Mons[tp->tm_mon]) {
  653. #if ENABLE_DEBUG_CROND_OPTION
  654. if (DebugOpt) {
  655. crondlog("\005 JobToDo: %d %s\n",
  656. line->cl_Pid, line->cl_Shell);
  657. }
  658. #endif
  659. if (line->cl_Pid > 0) {
  660. crondlog("\010 process already running: %s %s\n",
  661. file->cf_User, line->cl_Shell);
  662. } else if (line->cl_Pid == 0) {
  663. line->cl_Pid = -1;
  664. file->cf_Ready = 1;
  665. ++nJobs;
  666. }
  667. }
  668. }
  669. }
  670. }
  671. }
  672. return nJobs;
  673. }
  674. static void RunJobs(void)
  675. {
  676. CronFile *file;
  677. CronLine *line;
  678. for (file = FileBase; file; file = file->cf_Next) {
  679. if (file->cf_Ready) {
  680. file->cf_Ready = 0;
  681. for (line = file->cf_LineBase; line; line = line->cl_Next) {
  682. if (line->cl_Pid < 0) {
  683. RunJob(file->cf_User, line);
  684. crondlog("\010USER %s pid %3d cmd %s\n",
  685. file->cf_User, line->cl_Pid, line->cl_Shell);
  686. if (line->cl_Pid < 0) {
  687. file->cf_Ready = 1;
  688. }
  689. else if (line->cl_Pid > 0) {
  690. file->cf_Running = 1;
  691. }
  692. }
  693. }
  694. }
  695. }
  696. }
  697. /*
  698. * CheckJobs() - check for job completion
  699. *
  700. * Check for job completion, return number of jobs still running after
  701. * all done.
  702. */
  703. static int CheckJobs(void)
  704. {
  705. CronFile *file;
  706. CronLine *line;
  707. int nStillRunning = 0;
  708. for (file = FileBase; file; file = file->cf_Next) {
  709. if (file->cf_Running) {
  710. file->cf_Running = 0;
  711. for (line = file->cf_LineBase; line; line = line->cl_Next) {
  712. if (line->cl_Pid > 0) {
  713. int status;
  714. int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
  715. if (r < 0 || r == line->cl_Pid) {
  716. EndJob(file->cf_User, line);
  717. if (line->cl_Pid) {
  718. file->cf_Running = 1;
  719. }
  720. } else if (r == 0) {
  721. file->cf_Running = 1;
  722. }
  723. }
  724. }
  725. }
  726. nStillRunning += file->cf_Running;
  727. }
  728. return nStillRunning;
  729. }
  730. #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
  731. static void
  732. ForkJob(const char *user, CronLine * line, int mailFd,
  733. const char *prog, const char *cmd, const char *arg, const char *mailf)
  734. {
  735. /* Fork as the user in question and run program */
  736. pid_t pid = fork();
  737. line->cl_Pid = pid;
  738. if (pid == 0) {
  739. /* CHILD */
  740. /* Change running state to the user in question */
  741. if (ChangeUser(user) < 0) {
  742. exit(0);
  743. }
  744. #if ENABLE_DEBUG_CROND_OPTION
  745. if (DebugOpt) {
  746. crondlog("\005Child Running %s\n", prog);
  747. }
  748. #endif
  749. if (mailFd >= 0) {
  750. dup2(mailFd, mailf != NULL);
  751. dup2((mailf ? mailFd : 1), 2);
  752. close(mailFd);
  753. }
  754. execl(prog, prog, cmd, arg, NULL);
  755. crondlog("\024cannot exec, user %s cmd %s %s %s\n", user, prog, cmd, arg);
  756. if (mailf) {
  757. fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
  758. }
  759. exit(0);
  760. } else if (pid < 0) {
  761. /* FORK FAILED */
  762. crondlog("\024cannot fork, user %s\n", user);
  763. line->cl_Pid = 0;
  764. if (mailf) {
  765. remove(mailf);
  766. }
  767. } else if (mailf) {
  768. /* PARENT, FORK SUCCESS
  769. * rename mail-file based on pid of process
  770. */
  771. char mailFile2[128];
  772. snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid);
  773. rename(mailf, mailFile2);
  774. }
  775. /*
  776. * Close the mail file descriptor.. we can't just leave it open in
  777. * a structure, closing it later, because we might run out of descriptors
  778. */
  779. if (mailFd >= 0) {
  780. close(mailFd);
  781. }
  782. }
  783. static void RunJob(const char *user, CronLine * line)
  784. {
  785. char mailFile[128];
  786. int mailFd;
  787. line->cl_Pid = 0;
  788. line->cl_MailFlag = 0;
  789. /* open mail file - owner root so nobody can screw with it. */
  790. snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());
  791. mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
  792. if (mailFd >= 0) {
  793. line->cl_MailFlag = 1;
  794. fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
  795. line->cl_Shell);
  796. line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
  797. } else {
  798. crondlog("\024cannot create mail file user %s file %s, output to /dev/null\n", user, mailFile);
  799. }
  800. ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
  801. }
  802. /*
  803. * EndJob - called when job terminates and when mail terminates
  804. */
  805. static void EndJob(const char *user, CronLine * line)
  806. {
  807. int mailFd;
  808. char mailFile[128];
  809. struct stat sbuf;
  810. /* No job */
  811. if (line->cl_Pid <= 0) {
  812. line->cl_Pid = 0;
  813. return;
  814. }
  815. /*
  816. * End of job and no mail file
  817. * End of sendmail job
  818. */
  819. snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);
  820. line->cl_Pid = 0;
  821. if (line->cl_MailFlag != 1) {
  822. return;
  823. }
  824. line->cl_MailFlag = 0;
  825. /*
  826. * End of primary job - check for mail file. If size has increased and
  827. * the file is still valid, we sendmail it.
  828. */
  829. mailFd = open(mailFile, O_RDONLY);
  830. remove(mailFile);
  831. if (mailFd < 0) {
  832. return;
  833. }
  834. if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid || sbuf.st_nlink != 0 ||
  835. sbuf.st_size == line->cl_MailPos || !S_ISREG(sbuf.st_mode)) {
  836. close(mailFd);
  837. return;
  838. }
  839. ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
  840. }
  841. #else
  842. /* crond without sendmail */
  843. static void RunJob(const char *user, CronLine * line)
  844. {
  845. /* Fork as the user in question and run program */
  846. pid_t pid = fork();
  847. if (pid == 0) {
  848. /* CHILD */
  849. /* Change running state to the user in question */
  850. if (ChangeUser(user) < 0) {
  851. exit(0);
  852. }
  853. #if ENABLE_DEBUG_CROND_OPTION
  854. if (DebugOpt) {
  855. crondlog("\005Child Running %s\n", DEFAULT_SHELL);
  856. }
  857. #endif
  858. execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
  859. crondlog("\024cannot exec, user %s cmd %s -c %s\n", user,
  860. DEFAULT_SHELL, line->cl_Shell);
  861. exit(0);
  862. } else if (pid < 0) {
  863. /* FORK FAILED */
  864. crondlog("\024cannot, user %s\n", user);
  865. pid = 0;
  866. }
  867. line->cl_Pid = pid;
  868. }
  869. #endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */