#include #include #include #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #ifdef DEBUG #define debug_printf(args...) fprintf(stderr, ## args) #else #define debug_printf(args...) #endif #if defined(__APPLE__) #define st_mtim st_mtimespec #endif static int spawn_shell_pipe(char *const argv[], pid_t *pid, int outfd) { int pipefd[2]; posix_spawn_file_actions_t factions; if (pipe(pipefd) < 0) { warn("pipe"); return -1; } if (posix_spawn_file_actions_init(&factions) < 0) { warn("posix_spawn_file_actions_init"); goto err1; } if (outfd >=0 && posix_spawn_file_actions_adddup2(&factions, outfd, STDOUT_FILENO) < 0) { warn("posix_spawn_file_actions_adddup2"); goto err2; } if (posix_spawn_file_actions_adddup2(&factions, pipefd[0], 0) < 0) { warn("posix_spawn_file_actions_adddup2"); goto err2; } if (posix_spawn(pid, argv[0], &factions, NULL, argv, NULL) < 0) { warn("%s", argv[0]); goto err2; } close(pipefd[0]); return pipefd[1]; err2: posix_spawn_file_actions_destroy(&factions); err1: close(pipefd[0]); close(pipefd[1]); return -1; } static int read_apkbuild(int shellfd, size_t size, const char *name) { #define ECHO_SEPARATOR "echo \"-\"\n" #define ECHO_STR(var) "[ -n \"$" #var "\" ] && echo \" " #var ": $" #var "\"\n" #define ECHO_LIST(var) "[ -n \"$" #var "\" ] && echo ' " #var ":' && " \ "for i in $" #var "; do\n" \ "\techo \" - \\\"$i\\\"\"\n" \ "done\n" char *fmtbuf = "unset pkgname" " pkgver" " pkgrel" " arch" " options" " depends" " makedepends" " checkdepends" " subpackages" " source" " license" " url" "\n" "cd %s\n" "aports_cache_pwd=\"$PWD\"\n" ". ./APKBUILD >/dev/null\n" "if [ \"$PWD\" != \"$aports_cache_pwd\" ]; then\n" " echo \"%s: PWD changed\" >&2\n" " exit 1\n" "fi\n" ECHO_SEPARATOR ECHO_STR(pkgname) ECHO_STR(pkgver) ECHO_LIST(arch) ECHO_LIST(options) ECHO_LIST(depends) ECHO_LIST(makedepends) ECHO_LIST(checkdepends) ECHO_LIST(subpackages) ECHO_LIST(linuguas) ECHO_LIST(source) ECHO_STR(url) ECHO_STR(license) "cd ..\n\n"; char buf[strlen(fmtbuf) + 512]; size_t bufsize = snprintf(buf, sizeof(buf), fmtbuf, name, name); ssize_t r, offs=0; while (1) { r = write(shellfd, &buf[offs], bufsize - offs); if (r < 0) { warn("write to shell pipe"); return -1; } offs += r; if (r == 0 || offs == bufsize) break; } return 0; } static int is_newer(struct timespec a, struct timespec b) { return (a.tv_sec == b.tv_sec) ? a.tv_nsec > b.tv_nsec : a.tv_sec > b.tv_sec; } static int cache_refresh_or_check(int dirfd, const char *cachefile, int shellfd) { struct stat cache; DIR *dir; struct dirent *ent; int r = 0; int dirfd2 = dup(dirfd); if (shellfd == -1 && fstatat(dirfd, cachefile, &cache, 0) == -1) return 1; dir = fdopendir(dirfd2); if (dir == NULL) return -1; while(1) { char buf[PATH_MAX]; struct stat apkbuild; errno = 0; ent = readdir(dir); if (ent == NULL) { if (errno != 0) { warn("readdir"); closedir(dir); return -1; } break; } if (ent->d_name[0] == '.') continue; snprintf(buf, sizeof(buf), "%s/APKBUILD", ent->d_name); if (fstatat(dirfd, buf, &apkbuild, 0) == -1) { warn("%s", buf); continue; } if (shellfd != -1) { read_apkbuild(shellfd, apkbuild.st_size, ent->d_name); continue; } if (is_newer(apkbuild.st_mtim, cache.st_mtim)) { debug_printf("modified: %s\n", buf); r = 1; break; } } closedir(dir); return r; } int aports_cache_check(int dirfd, const char *cachefile) { return cache_refresh_or_check(dirfd, cachefile, -1); } int aports_cache_refresh(int dirfd, const char *cachefile, char *const shell_argv[]) { pid_t shell_pid; int shellfd, outfd = -1; int status; if (cachefile) { outfd = open(cachefile, O_WRONLY | O_CREAT, 0660); if (outfd < 0) { warn("%s", cachefile); return -1; } } shellfd = spawn_shell_pipe(shell_argv, &shell_pid, outfd); debug_printf("shell pid: %d\n", shell_pid); if (outfd >= 0) close(outfd); cache_refresh_or_check(dirfd, cachefile, shellfd); write(shellfd, "exit 0\n", 7); if (waitpid(shell_pid, &status, 0) < 0) { warn("waitpid"); return -1; } return WIFEXITED(status) ? WEXITSTATUS(status) : -1; }