run_parts.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini run-parts implementation for busybox
  4. *
  5. * Copyright (C) 2007 Bernhard Reutner-Fischer
  6. *
  7. * Based on a older version that was in busybox which was 1k big.
  8. * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
  9. *
  10. * Based on the Debian run-parts program, version 1.15
  11. * Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
  12. * Copyright (C) 1996-1999 Guy Maor <maor@debian.org>
  13. *
  14. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  15. */
  16. /* This is my first attempt to write a program in C (well, this is my first
  17. * attempt to write a program! :-) . */
  18. /* This piece of code is heavily based on the original version of run-parts,
  19. * taken from debian-utils. I've only removed the long options and the
  20. * report mode. As the original run-parts support only long options, I've
  21. * broken compatibility because the BusyBox policy doesn't allow them.
  22. */
  23. //usage:#define run_parts_trivial_usage
  24. //usage: "[-a ARG]... [-u UMASK] "
  25. //usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS("[--reverse] [--test] [--exit-on-error] "IF_FEATURE_RUN_PARTS_FANCY("[--list] "))
  26. //usage: "DIRECTORY"
  27. //usage:#define run_parts_full_usage "\n\n"
  28. //usage: "Run a bunch of scripts in DIRECTORY\n"
  29. //usage: "\n -a ARG Pass ARG as argument to scripts"
  30. //usage: "\n -u UMASK Set UMASK before running scripts"
  31. //usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS(
  32. //usage: "\n --reverse Reverse execution order"
  33. //usage: "\n --test Dry run"
  34. //usage: "\n --exit-on-error Exit if a script exits with non-zero"
  35. //usage: IF_FEATURE_RUN_PARTS_FANCY(
  36. //usage: "\n --list Print names of matching files even if they are not executable"
  37. //usage: )
  38. //usage: )
  39. //usage:
  40. //usage:#define run_parts_example_usage
  41. //usage: "$ run-parts -a start /etc/init.d\n"
  42. //usage: "$ run-parts -a stop=now /etc/init.d\n\n"
  43. //usage: "Let's assume you have a script foo/dosomething:\n"
  44. //usage: "#!/bin/sh\n"
  45. //usage: "for i in $*; do eval $i; done; unset i\n"
  46. //usage: "case \"$1\" in\n"
  47. //usage: "start*) echo starting something;;\n"
  48. //usage: "stop*) set -x; shutdown -h $stop;;\n"
  49. //usage: "esac\n\n"
  50. //usage: "Running this yields:\n"
  51. //usage: "$run-parts -a stop=+4m foo/\n"
  52. //usage: "+ shutdown -h +4m"
  53. #include "libbb.h"
  54. struct globals {
  55. char **names;
  56. int cur;
  57. char *cmd[2 /* using 1 provokes compiler warning */];
  58. } FIX_ALIASING;
  59. #define G (*(struct globals*)&bb_common_bufsiz1)
  60. #define names (G.names)
  61. #define cur (G.cur )
  62. #define cmd (G.cmd )
  63. #define INIT_G() do { } while (0)
  64. enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 };
  65. enum {
  66. OPT_a = (1 << 0),
  67. OPT_u = (1 << 1),
  68. OPT_r = (1 << 2) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
  69. OPT_t = (1 << 3) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
  70. OPT_e = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
  71. OPT_l = (1 << 5) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
  72. * ENABLE_FEATURE_RUN_PARTS_FANCY,
  73. };
  74. /* Is this a valid filename (upper/lower alpha, digits,
  75. * underscores, and hyphens only?)
  76. */
  77. static bool invalid_name(const char *c)
  78. {
  79. c = bb_basename(c);
  80. while (*c && (isalnum(*c) || *c == '_' || *c == '-'))
  81. c++;
  82. return *c; /* TRUE (!0) if terminating NUL is not reached */
  83. }
  84. static int bb_alphasort(const void *p1, const void *p2)
  85. {
  86. int r = strcmp(*(char **) p1, *(char **) p2);
  87. return (option_mask32 & OPT_r) ? -r : r;
  88. }
  89. static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth)
  90. {
  91. if (depth == 1)
  92. return TRUE;
  93. if (depth == 2
  94. && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK))
  95. || invalid_name(file)
  96. || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0))
  97. ) {
  98. return SKIP;
  99. }
  100. names = xrealloc_vector(names, 4, cur);
  101. names[cur++] = xstrdup(file);
  102. /*names[cur] = NULL; - xrealloc_vector did it */
  103. return TRUE;
  104. }
  105. #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
  106. static const char runparts_longopts[] ALIGN1 =
  107. "arg\0" Required_argument "a"
  108. "umask\0" Required_argument "u"
  109. //TODO: "verbose\0" No_argument "v"
  110. "reverse\0" No_argument "\xf0"
  111. "test\0" No_argument "\xf1"
  112. "exit-on-error\0" No_argument "\xf2"
  113. #if ENABLE_FEATURE_RUN_PARTS_FANCY
  114. "list\0" No_argument "\xf3"
  115. #endif
  116. ;
  117. #endif
  118. int run_parts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  119. int run_parts_main(int argc UNUSED_PARAM, char **argv)
  120. {
  121. const char *umask_p = "22";
  122. llist_t *arg_list = NULL;
  123. unsigned n;
  124. int ret;
  125. INIT_G();
  126. #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
  127. applet_long_options = runparts_longopts;
  128. #endif
  129. /* We require exactly one argument: the directory name */
  130. opt_complementary = "=1:a::";
  131. getopt32(argv, "a:u:", &arg_list, &umask_p);
  132. umask(xstrtou_range(umask_p, 8, 0, 07777));
  133. n = 1;
  134. while (arg_list && n < NUM_CMD) {
  135. cmd[n++] = llist_pop(&arg_list);
  136. }
  137. /* cmd[n] = NULL; - is already zeroed out */
  138. /* run-parts has to sort executables by name before running them */
  139. recursive_action(argv[optind],
  140. ACTION_RECURSE|ACTION_FOLLOWLINKS,
  141. act, /* file action */
  142. act, /* dir action */
  143. NULL, /* user data */
  144. 1 /* depth */
  145. );
  146. if (!names)
  147. return 0;
  148. qsort(names, cur, sizeof(char *), bb_alphasort);
  149. n = 0;
  150. while (1) {
  151. char *name = *names++;
  152. if (!name)
  153. break;
  154. if (option_mask32 & (OPT_t | OPT_l)) {
  155. puts(name);
  156. continue;
  157. }
  158. cmd[0] = name;
  159. ret = spawn_and_wait(cmd);
  160. if (ret == 0)
  161. continue;
  162. n = 1;
  163. if (ret < 0)
  164. bb_perror_msg("can't execute '%s'", name);
  165. else /* ret > 0 */
  166. bb_error_msg("%s exited with code %d", name, ret & 0xff);
  167. if (option_mask32 & OPT_e)
  168. xfunc_die();
  169. }
  170. return n;
  171. }