diff options
Diffstat (limited to 'util.c')
-rw-r--r-- | util.c | 356 |
1 files changed, 356 insertions, 0 deletions
@@ -0,0 +1,356 @@ +/* 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 <old mask>: %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 <old mask>: %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 <pipe> <stdout>: %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 <subprocess> 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, "<lat>"); + if (!p) + p = strstr(s, "<latitude>"); + 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, "<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]; + 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); +} |