# HG changeset patch # User Guido Berhoerster # Date 1504298021 -7200 # Node ID 5c6155c8e9b68169ed049160274dc6141e92fcce # Parent 1e39a251cbe9fa672fc18d736b1fc8ac3dda03c4 Handle signals Handled signals are generally blocked and only unblocked when doing blocking I/O, i.e. either when reading commands or printing results. A (possibly queued) signal will then interrupt I/O and can be dealt with in the main loop. diff -r 1e39a251cbe9 -r 5c6155c8e9b6 Makefile --- a/Makefile Thu Aug 24 13:10:56 2017 +0200 +++ b/Makefile Fri Sep 01 22:33:41 2017 +0200 @@ -79,6 +79,7 @@ ifeq ($(OS_NAME),Linux) HAVE_ARC4RANDOM ?= 0 HAVE_ASPRINTF ?= 1 + HAVE_CLOSEFROM ?= 0 HAVE_ERR_H ?= 1 HAVE_GETRANDOM ?= 0 HAVE_SYS_QUEUE_H ?= 0 @@ -87,6 +88,7 @@ else ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly),) HAVE_ARC4RANDOM ?= 1 HAVE_ASPRINTF ?= 1 + HAVE_CLOSEFROM ?= 1 HAVE_ERR_H ?= 1 HAVE_GETRANDOM ?= 0 HAVE_SYS_QUEUE_H ?= 1 @@ -95,6 +97,7 @@ else ifeq ($(OS_NAME),NetBSD) HAVE_ARC4RANDOM ?= 1 HAVE_ASPRINTF ?= 1 + HAVE_CLOSEFROM ?= 1 HAVE_ERR_H ?= 1 HAVE_GETRANDOM ?= 0 HAVE_SYS_QUEUE_H ?= 1 @@ -103,6 +106,7 @@ else ifeq ($(OS_NAME),OpenBSD) HAVE_ARC4RANDOM ?= 1 HAVE_ASPRINTF ?= 1 + HAVE_CLOSEFROM ?= 1 HAVE_ERR_H ?= 1 HAVE_GETRANDOM ?= 0 HAVE_SYS_QUEUE_H ?= 1 @@ -120,12 +124,14 @@ HAVE_ERR_H ?= 1 HAVE_GETRANDOM ?= 1 endif + HAVE_CLOSEFROM ?= 1 HAVE_SYS_QUEUE_H ?= 0 HAVE_SYS_TREE_H ?= 0 HAVE_SETPROGNAME ?= 0 else HAVE_ARC4RANDOM ?= 0 HAVE_ASPRINTF ?= 0 + HAVE_CLOSEFROM ?= 0 HAVE_ERR_H ?= 0 HAVE_GETRANDOM ?= 0 HAVE_SYS_QUEUE_H ?= 0 @@ -134,7 +140,9 @@ endif OBJS = cmd.o \ + io.o \ pager.o \ + proc.o \ pw.o \ pwfile.o \ pwm.o \ @@ -166,6 +174,11 @@ else OBJS += rand-dev-random.o endif +ifeq ($(HAVE_CLOSEFROM),1) + XCPPFLAGS += -DHAVE_CLOSEFROM +else + OBJS += compat/closefrom.o +endif ifeq ($(HAVE_ERR_H),1) XCPPFLAGS += -DHAVE_ERR_H else diff -r 1e39a251cbe9 -r 5c6155c8e9b6 cmd.c --- a/cmd.c Thu Aug 24 13:10:56 2017 +0200 +++ b/cmd.c Fri Sep 01 22:33:41 2017 +0200 @@ -36,7 +36,9 @@ #include #include "cmd.h" +#include "io.h" #include "pager.h" +#include "proc.h" #include "pw.h" #include "pwfile.h" #include "util.h" @@ -222,10 +224,14 @@ } if (ctx->errmsg != NULL) { - printf("%s\n", ctx->errmsg); + if (io_printf("%s\n", ctx->errmsg) == IO_SIGNAL) { + return (CMD_SIGNAL); + } } - printf("There are%sunsaved changes\n", ctx->unsaved_changes ? " " : - " no "); + if (io_printf("There are%sunsaved changes\n", + ctx->unsaved_changes ? " " : " no ") == IO_SIGNAL) { + return (CMD_SIGNAL); + } return (CMD_STATUS); } @@ -233,6 +239,7 @@ static enum cmd_return cmd_info(struct pwm_ctx *ctx, int argc, char *argv[]) { + enum cmd_return retval; struct metadata *metadata; struct pager *pager; struct tm *tm; @@ -244,7 +251,7 @@ metadata = pwfile_get_metadata(ctx); - pager = pager_create(stdout); + pager = pager_create(STDOUT_FILENO); pager_printf(pager, "Format: 0x%04x\n", metadata->version); if (metadata->user != NULL) { pager_printf(pager, "User: %s\n", metadata->user); @@ -258,12 +265,12 @@ tm = gmtime(&metadata->timestamp); strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm); pager_printf(pager, "Last Saved: %s\n", timebuf); - pager_show(pager); + retval = (pager_show(pager) != IO_SIGNAL) ? CMD_OK : CMD_SIGNAL; pager_destroy(pager); pwfile_destroy_metadata(metadata); - return (CMD_OK); + return (retval); } static enum cmd_return @@ -338,7 +345,7 @@ } } - pager = pager_create(stdout); + pager = pager_create(STDOUT_FILENO); list = pwfile_create_list(ctx); for (j = 0; list[j] != NULL; j++) { if (list[j]->any.type == ITEM_TYPE_GROUP) { @@ -363,9 +370,7 @@ pwfile_destroy_record(record); } } - pager_show(pager); - - retval = CMD_OK; + retval = (pager_show(pager) != IO_SIGNAL) ? CMD_OK : CMD_SIGNAL; out: pager_destroy(pager); @@ -633,12 +638,11 @@ pwm_err(ctx, "record %u does not exist", id); goto out; } + retval = CMD_OK; } else { - printf("%s\n", password); + retval = io_printf("%s\n", password); } - retval = CMD_OK; - out: free(char_groupv); @@ -667,14 +671,15 @@ return (CMD_OK); } -static void -print_record(struct record *record, int fields[], int show_labels, FILE *fp) +static int +print_record(struct record *record, int fields[], int show_labels, int fd) { struct pager *pager; struct tm *tm; char timebuf[TIME_SIZE]; + int retval; - pager = pager_create(fp); + pager = pager_create(fd); if (fields[FIELD_TITLE]) { pager_printf(pager, "%s%s\n", show_labels ? field_labels[FIELD_TITLE] : "", (record->title != NULL) ? @@ -717,13 +722,16 @@ pager_printf(pager, "%s%s\n", show_labels ? field_labels[FIELD_MTIME] : "", timebuf); } - pager_show(pager); + retval = pager_show(pager); pager_destroy(pager); + + return (retval); } static enum cmd_return cmd_show(struct pwm_ctx *ctx, int argc, char *argv[]) { + enum cmd_return retval; unsigned int id; struct record *record; int i; @@ -767,10 +775,11 @@ pwm_err(ctx, "record %u does not exist", id); return (CMD_ERR); } - print_record(record, fields, 1, stdout); + retval = (print_record(record, fields, 1, STDOUT_FILENO) != IO_SIGNAL) ? + CMD_OK : CMD_SIGNAL; pwfile_destroy_record(record); - return (CMD_OK); + return (retval); } static enum cmd_return @@ -781,7 +790,7 @@ struct record *record = NULL; enum field_type type; int fields[COUNTOF(field_namev) - 1] = { 0 }; - FILE *fp = NULL; + struct proc proc = { 0 }; if (argc != 4) { return (CMD_USAGE); @@ -799,9 +808,7 @@ } fields[type] = 1; - fp = popen(argv[3], "w"); - if (fp == NULL) { - warn("popen"); + if (proc_open(&proc, argv[3], "w") != IO_OK) { goto out; } @@ -811,14 +818,15 @@ goto out; } - print_record(record, fields, 0, fp); - - retval = CMD_OK; + retval = (print_record(record, fields, 0, proc.fd) != IO_SIGNAL) ? + CMD_OK : CMD_SIGNAL; out: pwfile_destroy_record(record); - if (fp != NULL) { - pclose(fp); + if (proc.pid != 0) { + if (proc_close(&proc) == IO_SIGNAL) { + retval = CMD_SIGNAL; + } } return (retval); @@ -883,6 +891,7 @@ static enum cmd_return cmd_help(struct pwm_ctx *ctx, int argc, char *argv[]) { + enum cmd_return retval = CMD_OK; struct pager *pager; struct cmd *cmd; @@ -890,7 +899,7 @@ return (CMD_USAGE); } - pager = pager_create(stdout); + pager = pager_create(STDOUT_FILENO); if (argc == 2) { for (cmd = cmds; cmd->cmd_func != NULL; cmd++) { if ((strcmp(argv[1], cmd->abbrev_cmd) == 0) || @@ -906,10 +915,10 @@ cmd->full_cmd, cmd->description); } } - pager_show(pager); + retval = (pager_show(pager) != IO_SIGNAL) ? CMD_OK : CMD_SIGNAL; pager_destroy(pager); - return (CMD_OK); + return (retval); } static enum cmd_return diff -r 1e39a251cbe9 -r 5c6155c8e9b6 cmd.h --- a/cmd.h Thu Aug 24 13:10:56 2017 +0200 +++ b/cmd.h Fri Sep 01 22:33:41 2017 +0200 @@ -31,6 +31,7 @@ CMD_STATUS, CMD_ERR, CMD_USAGE, + CMD_SIGNAL, CMD_QUIT }; diff -r 1e39a251cbe9 -r 5c6155c8e9b6 compat.h --- a/compat.h Thu Aug 24 13:10:56 2017 +0200 +++ b/compat.h Fri Sep 01 22:33:41 2017 +0200 @@ -31,6 +31,10 @@ #include "compat/asprintf.h" #endif /* !HAVE_ASPRINTF */ +#ifndef HAVE_CLOSEFROM +#include "compat/closefrom.h" +#endif /* !HAVE_CLOSEFROM */ + #ifndef HAVE_ERR_H #include "compat/err.h" #endif /* !HAVE_ERR_H */ diff -r 1e39a251cbe9 -r 5c6155c8e9b6 compat/closefrom.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/closefrom.c Fri Sep 01 22:33:41 2017 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +int +closefrom(int fd_min) +{ +#ifdef F_CLOSEM + return fcntl(fd_min, F_CLOSEM, 0); +#else /* !F_CLOSEM */ + int fd_max; + int fd; + + fd_max = sysconf(_SC_OPEN_MAX); + for (fd = fd_min; fd < fd_max; fd++) { + if ((close(fd) < 0) && (errno != EBADF)) { + return (-1); + } + } + + return (0); +#endif /* !F_CLOSEM */ +} diff -r 1e39a251cbe9 -r 5c6155c8e9b6 compat/closefrom.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/closefrom.h Fri Sep 01 22:33:41 2017 +0200 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef CLOSEFROM_H +#define CLOSEFROM_H + +int closefrom(int); + +#endif /* !CLOSEFROM_H */ diff -r 1e39a251cbe9 -r 5c6155c8e9b6 io.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/io.c Fri Sep 01 22:33:41 2017 +0200 @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2017 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "compat.h" + +#ifdef HAVE_ERR_H +#include +#endif /* HAVE_ERR_H */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "io.h" +#include "util.h" +#include "pwm.h" + +static sigjmp_buf signal_env; + +static void +signal_handler(int signal_no) +{ + siglongjmp(signal_env, signal_no); +} + +int +io_gl_complete_nothing(WordCompletion *cpl, void *data, const char *line, + int word_end) +{ + return (0); +} + +enum io_status +io_get_char(const char *prompt, int *cp) +{ + enum io_status retval = IO_OK; + GetLine *gl = NULL; + + gl = new_GetLine(16, 0); + if (gl== NULL) { + err(1, "new_GetLine"); + } + gl_catch_blocked(gl); + + /* prompt with echo off */ + gl_echo_mode(gl, 0); + if ((*cp = gl_query_char(gl, prompt, '\0')) == EOF) { + switch (gl_return_status(gl)) { + case GLR_SIGNAL: + retval = IO_SIGNAL; + break; + case GLR_ERROR: + errx(1, "gl_get_line: %s", + gl_error_message(gl, NULL, 0)); + default: + errx(1, "unknown error in gl_get_line"); + } + } + + /* erase prompt */ + if (io_printf("\r%*s\r", (int)strlen(prompt), "") == IO_SIGNAL) { + retval = IO_SIGNAL; + } + + del_GetLine(gl); + + return (retval); +} + +enum io_status +io_get_line(GetLine *gl, const char *prompt, int with_history, + const char *start_line, int start_pos, size_t buf_size, char *buf) +{ + enum io_status retval = IO_OK; + GetLine *gl_private = NULL; + GlHistoryState state; + char *line; + + if (gl == NULL) { + gl = gl_private = new_GetLine(buf_size - 1, 0); + if (gl_private == NULL) { + err(1, "new_GetLine"); + } + gl_catch_blocked(gl_private); + } + + gl_state_of_history(gl, &state); + gl_toggle_history(gl, with_history); + + line = gl_get_line(gl, prompt, start_line, start_pos); + if (line == NULL) { + switch (gl_return_status(gl)) { + case GLR_BLOCKED: + break; + case GLR_SIGNAL: + retval = IO_SIGNAL; + goto out; + case GLR_EOF: + retval = IO_EOF; + goto out; + case GLR_ERROR: + errx(1, "gl_get_line: %s", + gl_error_message(gl, NULL, 0)); + default: + errx(1, "unknown error in gl_get_line"); + } + } + + if (snprintf(buf, buf_size, "%s", line) >= (int)buf_size) { + retval = IO_TRUNCATED; + } + +out: + if (gl != NULL) { + gl_toggle_history(gl, state.enabled); + } + del_GetLine(gl_private); + + return (retval); +} + +enum io_status +io_get_password(const char *prompt, const char *confirm_prompt, + size_t buf_size, char *buf) +{ + enum io_status retval = IO_OK; + GetLine *gl = NULL; + size_t len; + char *password_buf = NULL; + char *confirm_buf = NULL; + + gl = new_GetLine(buf_size - 1, 0); + if (gl == NULL) { + err(1, "new_GetLine"); + } + /* disable default filename completion */ + gl_customize_completion(gl, NULL, io_gl_complete_nothing); + gl_echo_mode(gl, 0); + + password_buf = xmalloc(buf_size); + + if (io_get_line(gl, prompt, 0, NULL, 0, buf_size, password_buf) == + IO_SIGNAL) { + retval = IO_SIGNAL; + goto out; + } + len = strlen(password_buf); + /* strip trailing newline */ + if ((len > 0) && (password_buf[len - 1] == '\n')) { + password_buf[--len] = '\0'; + } + if (len == 0) { + retval = IO_PASSWORD_EMPTY; + goto out; + } + + if (confirm_prompt != NULL) { + if (io_printf("\n") == IO_SIGNAL) { + retval = IO_SIGNAL; + goto out; + } + + /* confirm new password */ + confirm_buf = xmalloc(buf_size); + if (io_get_line(gl, confirm_prompt, 0, NULL, 0, + buf_size, confirm_buf) == IO_SIGNAL) { + retval = IO_SIGNAL; + goto out; + } + len = strlen(confirm_buf); + /* strip trailing newline */ + if ((len > 0) && (confirm_buf[len - 1] == '\n')) { + confirm_buf[--len] = '\0'; + } + if (strcmp(password_buf, confirm_buf) != 0) { + retval = IO_PASSWORD_MISMATCH; + goto out; + } + } + + strcpy(buf, password_buf); + +out: + if (io_printf("\n") == IO_SIGNAL) { + retval = IO_SIGNAL; + goto out; + } + free(password_buf); + free(confirm_buf); + del_GetLine(gl); + + return (retval); +} + +enum io_status +io_dputs(int fd, const char *s) +{ + struct sigaction action; + struct sigaction oaction; + int signal_no = 0; + const char *p = s; + size_t remaining; + ssize_t n; + + /* install signal handlers */ + action.sa_handler = signal_handler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + sigaddset(&action.sa_mask, SIGTERM); + sigaddset(&action.sa_mask, SIGHUP); + sigaddset(&action.sa_mask, SIGQUIT); + if ((sigaction(SIGINT, &action, &oaction) != 0) || + (sigaction(SIGTERM, &action, &oaction) != 0) || + (sigaction(SIGHUP, &action, &oaction) != 0) || + (sigaction(SIGQUIT, &action, &oaction) != 0)) { + err(1, "sigaction"); + } + + if ((signal_no = sigsetjmp(signal_env, 1)) != 0) { + /* signal received, signal mask has been restored */ + goto out; + } + + remaining = strlen(s); + while (remaining > 0) { + pwm_unblock_signals(); + n = write(fd, p, remaining); + if ((n < (int)remaining) && (errno != EINTR)) { + err(1, "write"); + } + pwm_block_signals(); + remaining -= MAX(n, 0); + p += MAX(n, 0); + } + +out: + /* restore signal handlers */ + if ((sigaction(SIGINT, &oaction, NULL) != 0) || + (sigaction(SIGTERM, &oaction, NULL) != 0) || + (sigaction(SIGHUP, &oaction, NULL) != 0) || + (sigaction(SIGQUIT, &oaction, NULL) != 0)) { + err(1, "sigaction"); + } + + return ((signal_no == 0) ? IO_OK : IO_SIGNAL); +} + +enum io_status +io_vdprintf(int fd, const char *fmt, va_list args) +{ + enum io_status retval; + char *buf; + + xvasprintf(&buf, fmt, args); + retval = io_dputs(fd, buf); + free(buf); + + return (retval); +} + +enum io_status +io_dprintf(int fd, const char *fmt, ...) +{ + enum io_status retval; + va_list args; + + va_start(args, fmt); + retval = io_vdprintf(fd, fmt, args); + va_end(args); + + return (retval); +} + +enum io_status +io_printf(const char *fmt, ...) +{ + enum io_status retval; + va_list args; + + va_start(args, fmt); + retval = io_vdprintf(STDOUT_FILENO, fmt, args); + va_end(args); + + return (retval); +} diff -r 1e39a251cbe9 -r 5c6155c8e9b6 io.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/io.h Fri Sep 01 22:33:41 2017 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef IO_H +#define IO_H + +#include +#include + +enum io_status { + IO_OK, + IO_EOF = -1, + IO_ERROR = -2, + IO_TRUNCATED = -3, + IO_SIGNAL = -4, + IO_PASSWORD_EMPTY = -5, + IO_PASSWORD_MISMATCH = -6 +}; + +int io_gl_complete_nothing(WordCompletion *, void *, const char *, + int); +enum io_status io_get_char(const char *, int *); +enum io_status io_get_line(GetLine *, const char *, int, const char *, int, + size_t, char *); +enum io_status io_get_password(const char *, const char *, size_t, char *); +enum io_status io_dputs(int, const char *); +enum io_status io_vdprintf(int, const char *, va_list); +enum io_status io_dprintf(int, const char *, ...); +enum io_status io_printf(const char *, ...); + +#endif /* !IO_H */ diff -r 1e39a251cbe9 -r 5c6155c8e9b6 pager.c --- a/pager.c Thu Aug 24 13:10:56 2017 +0200 +++ b/pager.c Fri Sep 01 22:33:41 2017 +0200 @@ -28,7 +28,6 @@ #endif /* HAVE_ERR_H */ #include #include -#include #include #include #include @@ -49,7 +48,7 @@ struct pager { TAILQ_HEAD(lines_head, lines_entry) lines_head; - FILE *fp; + int fd; size_t buf_size; char *buf; }; @@ -59,32 +58,14 @@ char *line; }; -static int -getch_prompt(GetLine *gl, const char *prompt) -{ - int c; - int saved_echo_mode; - - /* prompt with echo off */ - saved_echo_mode = gl_echo_mode(gl, -1); - gl_echo_mode(gl, 0); - c = gl_query_char(gl, prompt, '\0'); - gl_echo_mode(gl, saved_echo_mode); - - /* erase prompt */ - printf("\r%*s\r", (int)strlen(prompt), ""); - - return (c); -} - struct pager * -pager_create(FILE *fp) +pager_create(int fd) { struct pager *pager; pager = xmalloc(sizeof (struct pager)); TAILQ_INIT(&pager->lines_head); - pager->fp = fp; + pager->fd = fd; pager->buf_size = BUFSIZ; pager->buf = xmalloc(BUFSIZ); @@ -183,9 +164,10 @@ return (len); } -static unsigned int -mbsnprint(unsigned int cols, const char *s, FILE *fp) +static int +mbsnprint(unsigned int cols, const char *s, int fd) { + int retval; const char *p = s; unsigned int col = 0; int mb_len; @@ -232,21 +214,22 @@ p += mb_len; col += width; if (col <= cols) { - if (fputs(mb_buf, fp) == EOF) { - err(1, "fputs"); + retval = io_dputs(fd, mb_buf); + if (retval != IO_OK) { + return (retval); } } } - fputc('\n', fp); - fflush(fp); + retval = io_dputs(fd, "\n"); - return (col); + return (retval); } -void +enum io_status pager_show(struct pager *pager) { + int retval = IO_OK; int is_interactive; unsigned int rows = 24; unsigned int cols = 80; @@ -254,40 +237,45 @@ struct winsize ws; #endif /* TIOCGWINSZ */ unsigned int row = 0; - GetLine *gl; struct lines_entry *entry; + int c; - is_interactive = (isatty(STDIN_FILENO) && (pager->fp == stdout)); + is_interactive = (isatty(STDIN_FILENO) && (pager->fd == STDOUT_FILENO)); #ifdef TIOCGWINSZ if (is_interactive) { /* determine terminal size */ - if (ioctl(fileno(pager->fp), TIOCGWINSZ, &ws) == 0) { + if (ioctl(pager->fd, TIOCGWINSZ, &ws) == 0) { rows = (ws.ws_row > 0) ? ws.ws_row : rows; cols = (ws.ws_col > 0) ? ws.ws_col : cols; } } #endif /* TIOCGWINSZ */ - gl = new_GetLine(10, 0); - if (gl == NULL) { - err(1, "new_GetLine"); - } - TAILQ_FOREACH(entry, &pager->lines_head, entry) { if (is_interactive) { - mbsnprint(cols, entry->line, pager->fp); + retval = mbsnprint(cols, entry->line, pager->fd); + if (retval != IO_OK) { + goto out; + } row++; if ((TAILQ_NEXT(entry, entry) != NULL) && (row == rows - 1)) { - getch_prompt(gl, "--More--"); + /* prompt for keypress */ + retval = io_get_char("--More--", &c); + if (retval != IO_OK) { + goto out; + } row = 0; } } else { - fprintf(pager->fp, "%s", entry->line); - fflush(pager->fp); + retval = io_dputs(pager->fd, entry->line); + if (retval != IO_OK) { + goto out; + } } } - del_GetLine(gl); +out: + return (retval); } diff -r 1e39a251cbe9 -r 5c6155c8e9b6 pager.h --- a/pager.h Thu Aug 24 13:10:56 2017 +0200 +++ b/pager.h Fri Sep 01 22:33:41 2017 +0200 @@ -27,12 +27,14 @@ #include +#include "io.h" + struct pager; -struct pager * pager_create(FILE *fp); +struct pager * pager_create(int); void pager_destroy(struct pager *); int pager_vprintf(struct pager *, const char *, va_list); int pager_printf(struct pager *, const char *, ...); -void pager_show(struct pager *); +enum io_status pager_show(struct pager *); #endif /* !PAGER_H */ diff -r 1e39a251cbe9 -r 5c6155c8e9b6 proc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/proc.c Fri Sep 01 22:33:41 2017 +0200 @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "compat.h" + +#ifdef HAVE_ERR_H +#include +#endif /* HAVE_ERR_H */ +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "proc.h" +#include "pwm.h" + +#define PIPE_R 0 +#define PIPE_W 1 + +static sigjmp_buf signal_env; + +static void +signal_handler(int signal_no) +{ + siglongjmp(signal_env, signal_no); +} + +enum io_status +proc_open(struct proc *proc, const char *command, const char *type) +{ + int pipe_fds[2]; + pid_t pid; + + if ((strlen(type) != 1) || ((*type != 'r') && (*type != 'w'))) { + return (IO_ERROR); + } + + if (pipe(pipe_fds) < 0) { + return (IO_ERROR); + } + + switch (pid = fork()) { + case -1: + return (IO_ERROR); + case 0: + if (*type == 'r') { + close(pipe_fds[PIPE_R]); + dup2(pipe_fds[PIPE_W], STDOUT_FILENO); + } else { + close(pipe_fds[PIPE_W]); + dup2(pipe_fds[PIPE_R], STDIN_FILENO); + } + closefrom(STDERR_FILENO + 1); + execlp("sh", "sh", "-c", command, (char *)0); + err(1, "execlp"); + default: + if (*type == 'r') { + close(pipe_fds[PIPE_W]); + proc->fd = pipe_fds[PIPE_R]; + } else { + close(pipe_fds[PIPE_R]); + proc->fd = pipe_fds[PIPE_W]; + } + proc->pid = pid; + return (IO_OK); + } +} + +enum io_status +proc_close(struct proc *proc) +{ + struct sigaction action; + struct sigaction oaction; + int signal_no = 0; + pid_t wpid; + int status; + + close(proc->fd); + + /* install signal handlers */ + action.sa_handler = signal_handler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + sigaddset(&action.sa_mask, SIGTERM); + sigaddset(&action.sa_mask, SIGHUP); + sigaddset(&action.sa_mask, SIGQUIT); + if ((sigaction(SIGINT, &action, &oaction) != 0) || + (sigaction(SIGTERM, &action, &oaction) != 0) || + (sigaction(SIGHUP, &action, &oaction) != 0) || + (sigaction(SIGQUIT, &action, &oaction) != 0)) { + err(1, "sigaction"); + } + + if ((signal_no = sigsetjmp(signal_env, 1)) != 0) { + /* + * signal received, signal mask has been restored, send SIGTERM + * to the child process, wait 500 ms and send a SIGKILL if the + * child still exists + */ + kill(proc->pid, SIGTERM); + nanosleep(&(struct timespec){ .tv_nsec = 500 * 1000 * 1000 }, + NULL); + do { + wpid = waitpid(proc->pid, &status, WNOHANG); + } while ((wpid == -1) && (errno == EINTR)); + if (wpid == proc->pid) { + goto out; + } + + kill(proc->pid, SIGKILL); + do { + wpid = waitpid(proc->pid, &status, 0); + } while ((wpid == -1) && (errno == EINTR)); + + goto out; + } + + pwm_unblock_signals(); + do { + wpid = waitpid(proc->pid, &status, 0); + } while ((wpid == -1) && (errno == EINTR)); + pwm_block_signals(); + +out: + /* restore signal handlers */ + if ((sigaction(SIGINT, &oaction, NULL) != 0) || + (sigaction(SIGTERM, &oaction, NULL) != 0) || + (sigaction(SIGHUP, &oaction, NULL) != 0) || + (sigaction(SIGQUIT, &oaction, NULL) != 0)) { + err(1, "sigaction"); + } + + proc->fd = -1; + proc->pid = (pid_t)-1; + + return ((signal_no == 0) ? IO_OK : IO_SIGNAL); +} diff -r 1e39a251cbe9 -r 5c6155c8e9b6 proc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/proc.h Fri Sep 01 22:33:41 2017 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PROC_H +#define PROC_H + +#include "io.h" + +struct proc { + int fd; + pid_t pid; +}; + +enum io_status proc_open(struct proc *, const char *, const char *); +enum io_status proc_close(struct proc *); + +#endif /* !PROC_H */ diff -r 1e39a251cbe9 -r 5c6155c8e9b6 pwm.c --- a/pwm.c Thu Aug 24 13:10:56 2017 +0200 +++ b/pwm.c Fri Sep 01 22:33:41 2017 +0200 @@ -41,22 +41,11 @@ #include "pwm.h" #include "cmd.h" +#include "io.h" #include "pwfile.h" #include "tok.h" #include "util.h" -#ifndef PWM_HISTORY_ENTRIES_MAX -#define PWM_HISTORY_ENTRIES_MAX 1024 -#endif /* !PWM_HISTORY_MAX */ - -#ifndef PWM_HISTORY_LINES_MAX -#define PWM_HISTORY_LINES_MAX 256 -#endif /* !PWM_HISTORY_LINES_MAX */ - -#ifndef PWM_HISTORY_MAX -#define PWM_HISTORY_MAX (PWM_HISTORY_LINES_MAX * PWM_LINE_MAX) -#endif /* !PWM_HISTORY_MAX */ - static void usage(void) { @@ -81,11 +70,34 @@ } } -static int -complete_nothing(WordCompletion *cpl, void *data, const char *line, - int word_end) +void +pwm_block_signals(void) { - return (0); + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + sigaddset(&set, SIGQUIT); + if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) { + err(1, "sigprocmask"); + } +} + +void +pwm_unblock_signals(void) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + sigaddset(&set, SIGQUIT); + if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { + err(1, "sigprocmask"); + } } static int @@ -94,9 +106,8 @@ int retval = -1; char prompt[8 + 2 + 1]; GetLine *gl = NULL; - char buf[PWM_LINE_MAX]; - char *line = buf; - int c; + char buf[PWM_LINE_MAX + 1]; + int io_retval; int argc = 0; char **argv = NULL; struct cmd *cmd; @@ -104,31 +115,41 @@ snprintf(prompt, sizeof (prompt), "%.*s> ", 8, getprogname()); + pwm_block_signals(); + /* initialize libtecla */ gl = new_GetLine(PWM_LINE_MAX, PWM_HISTORY_MAX); if (gl == NULL) { err(1, "new_GetLine"); } + gl_catch_blocked(gl); gl_limit_history(gl, PWM_HISTORY_LINES_MAX); /* disable default filename completion */ - gl_customize_completion(gl, NULL, complete_nothing); + gl_customize_completion(gl, NULL, io_gl_complete_nothing); for (;;) { + /* read next line */ cmd = NULL; - line = gl_get_line(gl, prompt, NULL, -1); - if (line == NULL) { - switch (gl_return_status(gl)) { - case GLR_EOF: - break; - case GLR_ERROR: - warnx("gl_get_line: %s", - gl_error_message(gl, NULL, 0)); - } + buf[0] = '\0'; + io_retval = io_get_line(gl, prompt, 1, NULL, 0, + sizeof (buf), buf); + switch (io_retval) { + case IO_OK: break; + case IO_TRUNCATED: + /* line was truncated in non-interactive mode */ + fprintf(stderr, "line too long\n"); + goto out; + case IO_EOF: /* FALLTHROUGH */ + case IO_SIGNAL: + goto quit; + default: + fprintf(stderr, "unknown error\n"); + goto quit; } /* tokenize line */ - switch (tok_tokenize(line, &argc, &argv)) { + switch (tok_tokenize(buf, &argc, &argv)) { case TOK_ERR_SYSTEM_ERROR: err(1, "tok_tokenize"); case TOK_ERR_UNTERMINATED_QUOTE: @@ -171,7 +192,9 @@ goto out; } break; - case CMD_QUIT: + case CMD_SIGNAL: + fprintf(stderr, "received signal, quitting\n"); + case CMD_QUIT: /* FALLTHROUGH */ goto quit; } ctx->prev_cmd = cmd->full_cmd; @@ -201,69 +224,22 @@ int pwm_read_password(struct pwm_ctx *ctx, int is_new_password) { - int retval = -1; - GetLine *gl = NULL; - char *line; - size_t len; - char password_buf[sizeof (ctx->password)] = { '\0' }; - - /* initialize libtecla */ - gl = new_GetLine(sizeof (password_buf) - 1, 0); - if (gl == NULL) { - err(1, "new_GetLine"); - } - /* disable default filename completion */ - gl_customize_completion(gl, NULL, complete_nothing); - gl_echo_mode(gl, 0); - - line = gl_get_line(gl, is_new_password ? "New password: " : - "Password: ", NULL, -1); - putchar('\n'); - if (line == NULL) { - if (gl_return_status(gl) == GLR_ERROR) { - errx(1, "gl_get_line: %s", gl_error_message(gl, NULL, - 0)); - } - goto out; - } - len = strlen(line); - if ((len > 0) && (line[len - 1] == '\n')) { - line[--len] = '\0'; + switch (io_get_password(is_new_password ? "New Password:" : + "Password:", is_new_password ? "Confirm Password:" : NULL, + sizeof (ctx->password), ctx->password)) { + case IO_OK: + return (0); + case IO_SIGNAL: + return (-2); + case IO_PASSWORD_EMPTY: + pwm_err(ctx, "password must not be empty"); + return (-1); + case IO_PASSWORD_MISMATCH: + pwm_err(ctx, "passwords do not match"); + return (-1); + default: + return (-1); } - if (len == 0) { - fprintf(stderr, "password must not be empty\n"); - goto out; - } - strcpy(password_buf, line); - - /* confirm the entered password entered */ - if (is_new_password) { - line = gl_get_line(gl, "Confirm password: ", NULL, -1); - putchar('\n'); - if (line == NULL) { - if (gl_return_status(gl) == GLR_ERROR) { - errx(1, "gl_get_line: %s", gl_error_message(gl, - NULL, 0)); - } - goto out; - } - len = strlen(line); - if ((len > 0) && (line[len - 1] == '\n')) { - line[--len] = '\0'; - } - if (strcmp(password_buf, line) != 0) { - fprintf(stderr, "passwords do not match\n"); - goto out; - } - } - - strcpy(ctx->password, password_buf); - retval = 0; - -out: - del_GetLine(gl); - - return (retval); } static int diff -r 1e39a251cbe9 -r 5c6155c8e9b6 pwm.h --- a/pwm.h Thu Aug 24 13:10:56 2017 +0200 +++ b/pwm.h Fri Sep 01 22:33:41 2017 +0200 @@ -31,6 +31,18 @@ #define PWM_LINE_MAX 16384 #endif /* !PWM_LINE_MAX */ +#ifndef PWM_HISTORY_ENTRIES_MAX +#define PWM_HISTORY_ENTRIES_MAX 1024 +#endif /* !PWM_HISTORY_MAX */ + +#ifndef PWM_HISTORY_LINES_MAX +#define PWM_HISTORY_LINES_MAX 256 +#endif /* !PWM_HISTORY_LINES_MAX */ + +#ifndef PWM_HISTORY_MAX +#define PWM_HISTORY_MAX (PWM_HISTORY_LINES_MAX * PWM_LINE_MAX) +#endif /* !PWM_HISTORY_MAX */ + struct pwm_ctx { const char *prev_cmd; char *errmsg; @@ -43,6 +55,8 @@ }; void pwm_err(struct pwm_ctx *, char *, ...); +void pwm_block_signals(void); +void pwm_unblock_signals(void); int pwm_read_password(struct pwm_ctx *, int); #endif /* PWM_H */