exec-cmd.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/compiler.h>
  3. #include <linux/string.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include "subcmd-util.h"
  11. #include "exec-cmd.h"
  12. #include "subcmd-config.h"
  13. #define MAX_ARGS 32
  14. #define PATH_MAX 4096
  15. static const char *argv_exec_path;
  16. static const char *argv0_path;
  17. void exec_cmd_init(const char *exec_name, const char *prefix,
  18. const char *exec_path, const char *exec_path_env)
  19. {
  20. subcmd_config.exec_name = exec_name;
  21. subcmd_config.prefix = prefix;
  22. subcmd_config.exec_path = exec_path;
  23. subcmd_config.exec_path_env = exec_path_env;
  24. /* Setup environment variable for invoked shell script. */
  25. setenv("PREFIX", prefix, 1);
  26. }
  27. #define is_dir_sep(c) ((c) == '/')
  28. static int is_absolute_path(const char *path)
  29. {
  30. return path[0] == '/';
  31. }
  32. static const char *get_pwd_cwd(char *buf, size_t sz)
  33. {
  34. char *pwd;
  35. struct stat cwd_stat, pwd_stat;
  36. if (getcwd(buf, sz) == NULL)
  37. return NULL;
  38. pwd = getenv("PWD");
  39. if (pwd && strcmp(pwd, buf)) {
  40. stat(buf, &cwd_stat);
  41. if (!stat(pwd, &pwd_stat) &&
  42. pwd_stat.st_dev == cwd_stat.st_dev &&
  43. pwd_stat.st_ino == cwd_stat.st_ino) {
  44. strlcpy(buf, pwd, sz);
  45. }
  46. }
  47. return buf;
  48. }
  49. static const char *make_nonrelative_path(char *buf, size_t sz, const char *path)
  50. {
  51. if (is_absolute_path(path)) {
  52. if (strlcpy(buf, path, sz) >= sz)
  53. die("Too long path: %.*s", 60, path);
  54. } else {
  55. const char *cwd = get_pwd_cwd(buf, sz);
  56. if (!cwd)
  57. die("Cannot determine the current working directory");
  58. if (strlen(cwd) + strlen(path) + 2 >= sz)
  59. die("Too long path: %.*s", 60, path);
  60. strcat(buf, "/");
  61. strcat(buf, path);
  62. }
  63. return buf;
  64. }
  65. char *system_path(const char *path)
  66. {
  67. char *buf = NULL;
  68. if (is_absolute_path(path))
  69. return strdup(path);
  70. astrcatf(&buf, "%s/%s", subcmd_config.prefix, path);
  71. return buf;
  72. }
  73. const char *extract_argv0_path(const char *argv0)
  74. {
  75. const char *slash;
  76. if (!argv0 || !*argv0)
  77. return NULL;
  78. slash = argv0 + strlen(argv0);
  79. while (argv0 <= slash && !is_dir_sep(*slash))
  80. slash--;
  81. if (slash >= argv0) {
  82. argv0_path = strndup(argv0, slash - argv0);
  83. return argv0_path ? slash + 1 : NULL;
  84. }
  85. return argv0;
  86. }
  87. void set_argv_exec_path(const char *exec_path)
  88. {
  89. argv_exec_path = exec_path;
  90. /*
  91. * Propagate this setting to external programs.
  92. */
  93. setenv(subcmd_config.exec_path_env, exec_path, 1);
  94. }
  95. /* Returns the highest-priority location to look for subprograms. */
  96. char *get_argv_exec_path(void)
  97. {
  98. char *env;
  99. if (argv_exec_path)
  100. return strdup(argv_exec_path);
  101. env = getenv(subcmd_config.exec_path_env);
  102. if (env && *env)
  103. return strdup(env);
  104. return system_path(subcmd_config.exec_path);
  105. }
  106. static void add_path(char **out, const char *path)
  107. {
  108. if (path && *path) {
  109. if (is_absolute_path(path))
  110. astrcat(out, path);
  111. else {
  112. char buf[PATH_MAX];
  113. astrcat(out, make_nonrelative_path(buf, sizeof(buf), path));
  114. }
  115. astrcat(out, ":");
  116. }
  117. }
  118. void setup_path(void)
  119. {
  120. const char *old_path = getenv("PATH");
  121. char *new_path = NULL;
  122. char *tmp = get_argv_exec_path();
  123. add_path(&new_path, tmp);
  124. add_path(&new_path, argv0_path);
  125. free(tmp);
  126. if (old_path)
  127. astrcat(&new_path, old_path);
  128. else
  129. astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin");
  130. setenv("PATH", new_path, 1);
  131. free(new_path);
  132. }
  133. static const char **prepare_exec_cmd(const char **argv)
  134. {
  135. int argc;
  136. const char **nargv;
  137. for (argc = 0; argv[argc]; argc++)
  138. ; /* just counting */
  139. nargv = malloc(sizeof(*nargv) * (argc + 2));
  140. nargv[0] = subcmd_config.exec_name;
  141. for (argc = 0; argv[argc]; argc++)
  142. nargv[argc + 1] = argv[argc];
  143. nargv[argc + 1] = NULL;
  144. return nargv;
  145. }
  146. int execv_cmd(const char **argv) {
  147. const char **nargv = prepare_exec_cmd(argv);
  148. /* execvp() can only ever return if it fails */
  149. execvp(subcmd_config.exec_name, (char **)nargv);
  150. free(nargv);
  151. return -1;
  152. }
  153. int execl_cmd(const char *cmd,...)
  154. {
  155. int argc;
  156. const char *argv[MAX_ARGS + 1];
  157. const char *arg;
  158. va_list param;
  159. va_start(param, cmd);
  160. argv[0] = cmd;
  161. argc = 1;
  162. while (argc < MAX_ARGS) {
  163. arg = argv[argc++] = va_arg(param, char *);
  164. if (!arg)
  165. break;
  166. }
  167. va_end(param);
  168. if (MAX_ARGS <= argc) {
  169. fprintf(stderr, " Error: too many args to run %s\n", cmd);
  170. return -1;
  171. }
  172. argv[argc] = NULL;
  173. return execv_cmd(argv);
  174. }