/* See LICENSE file for copyright and license details. */ #include "common.h" char * libgeome_util_areadlink(struct libgeome_context *ctx, const char *path) { char *ret = NULL, *new; size_t size = 0; ssize_t r; do { new = realloc(ret, size += 128U); if (!new) { ctx->print_error(ctx, "realloc: %s\n", strerror(errno)); free(ret); return NULL; } ret = new; r = readlink(path, ret, size - 1U); if (r < 0) { ctx->print_error(ctx, "readlink %s: %s\n", path, strerror(errno)); free(ret); return NULL; } } while ((size_t)r >= size - 2U); ret[r] = '\0'; return ret; } int libgeome_util_safe_pipe(struct libgeome_context *ctx, int fds[2]) { int i, old_fd, new_fd, nul_fd = -1; if (pipe(fds)) { ctx->print_error(ctx, "pipe: %s\n", strerror(errno)); return -1; } if (fds[0] > 2 && fds[1] > 2) return 0; for (i = 0; i < 2; i++) { if (fds[i] > 2) continue; new_fd = fcntl(fds[i], F_DUPFD, 3); if (new_fd < 0) { ctx->print_error(ctx, "fcntl F_DUPFD 3: %s\n", strerror(errno)); goto fail; } old_fd = fds[i]; close(fds[i]); fds[i] = new_fd; nul_fd = open("/dev/null", O_RDWR); if (nul_fd != old_fd) { ctx->print_error(ctx, "open /dev/null O_RDWR: %s\n", strerror(errno)); goto fail; } } if (old_fd < 2) { new_fd = dup(nul_fd); if (new_fd < 0) { ctx->print_error(ctx, "duo: %s\n", strerror(errno)); goto fail; } if (new_fd > 2) close(new_fd); } return 0; fail: close(fds[0]); close(fds[1]); return -1; } void libgeome_util_spawn_async_kill(struct libgeome_context *ctx, const struct spawn_join_info *info, int signum) { (void) ctx; kill(info->pid, signum); } int libgeome_util_spawn_async_read(struct libgeome_context *ctx, const char *path, const char *const *argv, unsigned int alarm_seconds, struct spawn_join_info *info_out, int *fd_out) { struct sigaction sa, old_sa; sigset_t mask, old_mask; int fds[2], ignore_sigchld; pid_t pid; if (sigemptyset(&mask)) { ctx->print_error(ctx, "sigemptyset: %s\n", strerror(errno)); return -1; } if (sigaddset(&mask, SIGCHLD)) { ctx->print_error(ctx, "sigaddset SIGCHLD: %s\n", strerror(errno)); return -1; } if (sigprocmask(SIG_BLOCK, &mask, &old_mask)) { ctx->print_error(ctx, "sigprocmask SIG_BLOCK {SIGCHLD}: %s\n", strerror(errno)); return -1; } memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; if (sigaction(SIGCHLD, &sa, &old_sa)) { ctx->print_error(ctx, "sigaction SIGCHLD: %s\n", strerror(errno)); if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) { ctx->print_abort(ctx, "sigprocmask SIG_SETMASK : %s\n", strerror(errno)); abort(); } return -1; } ignore_sigchld = !(old_sa.sa_flags & SA_SIGINFO) && old_sa.sa_handler == SIG_IGN; if (!ignore_sigchld && sigaction(SIGCHLD, &old_sa, NULL)) { ctx->print_abort(ctx, "sigaction SIGCHLD: %s\n", strerror(errno)); abort(); } if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) { ctx->print_abort(ctx, "sigaction SIG_SETMASK : %s\n", strerror(errno)); abort(); } if (libgeome_util_safe_pipe(ctx, fds)) goto out; pid = fork(); switch (pid) { case -1: ctx->print_error(ctx, "fork: %s\n", strerror(errno)); close(fds[0]); close(fds[1]); goto out; case 0: close(fds[0]); if (dup2(fds[1], STDOUT_FILENO) != STDOUT_FILENO) { ctx->print_error(ctx, "dup2 : %s\n", strerror(errno)); _exit(127); } close(fds[1]); alarm(alarm_seconds); execvp(path, (const void *)argv); ctx->print_error(ctx, "execvp %s: %s\n", path, strerror(errno)); _exit(127); default: close(fds[1]); break; } info_out->pid = pid; info_out->ignore_sigchld = ignore_sigchld; *fd_out = fds[0]; return 0; out: if (ignore_sigchld) { sa.sa_handler = SIG_IGN; if (sigaction(SIGCHLD, &sa, NULL)) { ctx->print_abort(ctx, "sigaction SIGCHLD: %s\n", strerror(errno)); abort(); } while (waitpid(-1, NULL, WNOHANG) != -1); } return -1; } int libgeome_util_spawn_async_join(struct libgeome_context *ctx, const struct spawn_join_info *info, int *status_out) { struct sigaction sa; int ret = 0; if (waitpid(info->pid, status_out, 0) != info->pid) { ctx->print_error(ctx, "waitpid 0: %s\n", strerror(errno)); ret = -1; } if (info->ignore_sigchld) { memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_IGN; if (sigaction(SIGCHLD, &sa, NULL)) { ctx->print_abort(ctx, "sigaction SIGCHLD: %s\n", strerror(errno)); abort(); } while (waitpid(-1, NULL, WNOHANG) != -1); } return ret; } uint64_t libgeome_util_parse_xml(char *s, struct location *location_out) { uint64_t ret = 0; char *p = s; errno = 0; p = strstr(s, ""); if (!p) p = strstr(s, ""); if (!p) goto skip_latitude; p = &strchr(p, '>')[1]; while (isspace(*p)) p++; location_out->latitude = strtod(p, &p); if (errno) goto skip_latitude; while (isspace(*p)) p++; if (*p != '<') goto skip_latitude; ret |= LIBGEOME_DATUM_LATITUDE; skip_latitude: p = strstr(s, ""); if (!p) p = strstr(s, ""); if (!p) p = strstr(s, ""); if (!p) p = strstr(s, ""); if (!p) goto skip_longitude; p = &strchr(p, '>')[1]; while (isspace(*p)) p++; location_out->longitude = strtod(p, &p); if (errno) goto skip_longitude; while (isspace(*p)) p++; if (*p != '<') goto skip_longitude; ret |= LIBGEOME_DATUM_LONGITUDE; skip_longitude: return ret; } uint64_t libgeome_util_parse_json(char *s, struct location *location_out) { uint64_t ret = 0; char *p = s; errno = 0; p = strstr(s, "\"lat\""); if (!p) p = strstr(s, "\"latitude\""); if (!p) goto skip_latitude; p = &strchr(&p[1], '"')[1]; while (isspace(*p)) p++; if (*p++ != ':') goto skip_latitude; while (isspace(*p)) p++; location_out->latitude = strtod(p, &p); if (errno) goto skip_latitude; while (isspace(*p)) p++; if (*p != ',' && *p != '}') goto skip_latitude; ret |= LIBGEOME_DATUM_LATITUDE; skip_latitude: p = strstr(s, "\"lon\""); if (!p) p = strstr(s, "\"long\""); if (!p) p = strstr(s, "\"lng\""); if (!p) p = strstr(s, "\"longitude\""); if (!p) goto skip_longitude; p = &strchr(&p[1], '"')[1]; while (isspace(*p)) p++; if (*p++ != ':') goto skip_longitude; while (isspace(*p)) p++; location_out->longitude = strtod(p, &p); if (errno) goto skip_longitude; while (isspace(*p)) p++; if (*p != ',' && *p != '}') goto skip_longitude; ret |= LIBGEOME_DATUM_LONGITUDE; skip_longitude: return ret; } uint64_t libgeome_util_parse_plain(char *s, struct location *location_out) { errno = 0; location_out->latitude = strtod(s, &s); if (errno) return 0; while (isspace(*s)) s++; if (*s == ',') s++; while (isspace(*s)) s++; location_out->longitude = strtod(s, &s); if (errno) return 0; while (isspace(*s)) s++; if (*s) return 0; return LIBGEOME_DATUM_LATITUDE | LIBGEOME_DATUM_LONGITUDE; } uint64_t libgeome_util_parse_output(char *s, struct location *location_out) { if (s[0] == '<') return libgeome_util_parse_xml(s, location_out); else if (s[0] == '{') return libgeome_util_parse_json(s, location_out); else return libgeome_util_parse_plain(s, location_out); }