#define _DEFAULT_SOURCE #include #include #include #include #include #include #include #define exit_with(f) \ exit_code = f; \ goto cleanup; #define throw_on(f, e, c) \ if (f == e) { \ exit_with(c); \ } #define free_if_used(f) \ if (f != NULL) { \ free(f); \ } int main(int argc, char *argv[]) { int exit_code = 0; if (argc < 2) { exit(EINVAL); } // open file char *script_path = argv[1]; FILE *script = fopen(script_path, "r"); if (script == NULL) { exit_with(errno); } // init variables char found_path [PATH_MAX+1]; int nargc = 0; char *args [128]; // read magic bytes size_t read = 0; char *line = NULL; read = getline(&line, &read, script); if (read == -1) { exit_with(errno); } // close file throw_on(fclose(script), EOF, EIO); if (strncmp(line, "#?", 2) != 0) { exit_with(ENOEXEC); } // read argv[0] char *token = strtok(&line[2], " "); throw_on(token, NULL, EINVAL); args[nargc] = malloc(sizeof(char) * strlen(token)); // avoid copying the magic bytes and newline char* p = mempcpy(args[nargc], token, strlen(token) - 1); *p = '\0'; token = strtok(NULL, " "); ++nargc; // collect rest of args while (token != NULL && nargc < 127) { args[nargc] = malloc(sizeof(char) * (strlen(token) + 1)); strcpy(args[nargc++], token); token = strtok(NULL, " "); } args[nargc] = script_path; // search for path env var char *path_env = getenv("PATH"); if (path_env == NULL) { exit(EINVAL); } // split it by colon token = strtok(path_env, ":"); // search each member of PATH for executable with name struct dirent *entity; DIR *dir; char rpath [PATH_MAX+1]; while (token != NULL) { char* __attribute__((unused)) _ = realpath(token, rpath); dir = opendir(token); if (dir != NULL) { while ((entity = readdir(dir)) != NULL) { if (entity->d_type == DT_REG && strcmp(args[0], entity->d_name) == 0) { throw_on(closedir(dir), -1, EIO); int len = strlen(token) + strlen(args[0]) + 2; snprintf(found_path, len, "%s/%s", token, args[0]); goto end_loop; } } throw_on(closedir(dir), -1, EIO); } token = strtok(NULL, ":"); } end_loop: if (strlen(found_path) == 0) { exit_with(ENOENT); } cleanup: free_if_used(line); if (exit_code == 0) { if (execv(found_path, args) == -1) { exit(errno); } } for (int n = 0; n < nargc; ++n) { free_if_used(args[nargc]); } return exit_code; }