exec-cmd.c 4.1 KB

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