projects/pwm

changeset 19:5c6155c8e9b6

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.
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Fri Sep 01 22:33:41 2017 +0200 (2017-09-01)
parents 1e39a251cbe9
children efef93e54c5f
files Makefile cmd.c cmd.h compat.h compat/closefrom.c compat/closefrom.h io.c io.h pager.c pager.h proc.c proc.h pwm.c pwm.h
line diff
     1.1 --- a/Makefile	Thu Aug 24 13:10:56 2017 +0200
     1.2 +++ b/Makefile	Fri Sep 01 22:33:41 2017 +0200
     1.3 @@ -79,6 +79,7 @@
     1.4  ifeq ($(OS_NAME),Linux)
     1.5    HAVE_ARC4RANDOM ?=	0
     1.6    HAVE_ASPRINTF ?=	1
     1.7 +  HAVE_CLOSEFROM ?=	0
     1.8    HAVE_ERR_H ?=		1
     1.9    HAVE_GETRANDOM ?=	0
    1.10    HAVE_SYS_QUEUE_H ?=	0
    1.11 @@ -87,6 +88,7 @@
    1.12  else ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly),)
    1.13    HAVE_ARC4RANDOM ?=	1
    1.14    HAVE_ASPRINTF ?=	1
    1.15 +  HAVE_CLOSEFROM ?=	1
    1.16    HAVE_ERR_H ?=		1
    1.17    HAVE_GETRANDOM ?=	0
    1.18    HAVE_SYS_QUEUE_H ?=	1
    1.19 @@ -95,6 +97,7 @@
    1.20  else ifeq ($(OS_NAME),NetBSD)
    1.21    HAVE_ARC4RANDOM ?=	1
    1.22    HAVE_ASPRINTF ?=	1
    1.23 +  HAVE_CLOSEFROM ?=	1
    1.24    HAVE_ERR_H ?=		1
    1.25    HAVE_GETRANDOM ?=	0
    1.26    HAVE_SYS_QUEUE_H ?=	1
    1.27 @@ -103,6 +106,7 @@
    1.28  else ifeq ($(OS_NAME),OpenBSD)
    1.29    HAVE_ARC4RANDOM ?=	1
    1.30    HAVE_ASPRINTF ?=	1
    1.31 +  HAVE_CLOSEFROM ?=	1
    1.32    HAVE_ERR_H ?=		1
    1.33    HAVE_GETRANDOM ?=	0
    1.34    HAVE_SYS_QUEUE_H ?=	1
    1.35 @@ -120,12 +124,14 @@
    1.36      HAVE_ERR_H ?=	1
    1.37      HAVE_GETRANDOM ?=	1
    1.38    endif
    1.39 +  HAVE_CLOSEFROM ?=	1
    1.40    HAVE_SYS_QUEUE_H ?=	0
    1.41    HAVE_SYS_TREE_H ?=	0
    1.42    HAVE_SETPROGNAME ?=	0
    1.43  else
    1.44    HAVE_ARC4RANDOM ?=	0
    1.45    HAVE_ASPRINTF ?=	0
    1.46 +  HAVE_CLOSEFROM ?=	0
    1.47    HAVE_ERR_H ?=		0
    1.48    HAVE_GETRANDOM ?=	0
    1.49    HAVE_SYS_QUEUE_H ?=	0
    1.50 @@ -134,7 +140,9 @@
    1.51  endif
    1.52  
    1.53  OBJS =	cmd.o \
    1.54 +	io.o \
    1.55  	pager.o \
    1.56 +	proc.o \
    1.57  	pw.o \
    1.58  	pwfile.o \
    1.59  	pwm.o \
    1.60 @@ -166,6 +174,11 @@
    1.61  else
    1.62    OBJS +=	rand-dev-random.o
    1.63  endif
    1.64 +ifeq ($(HAVE_CLOSEFROM),1)
    1.65 +  XCPPFLAGS +=	-DHAVE_CLOSEFROM
    1.66 +else
    1.67 +  OBJS +=	compat/closefrom.o
    1.68 +endif
    1.69  ifeq ($(HAVE_ERR_H),1)
    1.70    XCPPFLAGS +=	-DHAVE_ERR_H
    1.71  else
     2.1 --- a/cmd.c	Thu Aug 24 13:10:56 2017 +0200
     2.2 +++ b/cmd.c	Fri Sep 01 22:33:41 2017 +0200
     2.3 @@ -36,7 +36,9 @@
     2.4  #include <unistd.h>
     2.5  
     2.6  #include "cmd.h"
     2.7 +#include "io.h"
     2.8  #include "pager.h"
     2.9 +#include "proc.h"
    2.10  #include "pw.h"
    2.11  #include "pwfile.h"
    2.12  #include "util.h"
    2.13 @@ -222,10 +224,14 @@
    2.14  	}
    2.15  
    2.16  	if (ctx->errmsg != NULL) {
    2.17 -		printf("%s\n", ctx->errmsg);
    2.18 +		if (io_printf("%s\n", ctx->errmsg) == IO_SIGNAL) {
    2.19 +			return (CMD_SIGNAL);
    2.20 +		}
    2.21  	}
    2.22 -	printf("There are%sunsaved changes\n", ctx->unsaved_changes ? " " :
    2.23 -	    " no ");
    2.24 +	if (io_printf("There are%sunsaved changes\n",
    2.25 +	    ctx->unsaved_changes ? " " : " no ") == IO_SIGNAL) {
    2.26 +		return (CMD_SIGNAL);
    2.27 +	}
    2.28  
    2.29  	return (CMD_STATUS);
    2.30  }
    2.31 @@ -233,6 +239,7 @@
    2.32  static enum cmd_return
    2.33  cmd_info(struct pwm_ctx *ctx, int argc, char *argv[])
    2.34  {
    2.35 +	enum cmd_return	retval;
    2.36  	struct metadata	*metadata;
    2.37  	struct pager	*pager;
    2.38  	struct tm	*tm;
    2.39 @@ -244,7 +251,7 @@
    2.40  
    2.41  	metadata = pwfile_get_metadata(ctx);
    2.42  
    2.43 -	pager = pager_create(stdout);
    2.44 +	pager = pager_create(STDOUT_FILENO);
    2.45  	pager_printf(pager, "Format:      0x%04x\n", metadata->version);
    2.46  	if (metadata->user != NULL) {
    2.47  		pager_printf(pager, "User:        %s\n", metadata->user);
    2.48 @@ -258,12 +265,12 @@
    2.49  	tm = gmtime(&metadata->timestamp);
    2.50  	strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm);
    2.51  	pager_printf(pager, "Last Saved:  %s\n", timebuf);
    2.52 -	pager_show(pager);
    2.53 +	retval = (pager_show(pager) != IO_SIGNAL) ? CMD_OK : CMD_SIGNAL;
    2.54  	pager_destroy(pager);
    2.55  
    2.56  	pwfile_destroy_metadata(metadata);
    2.57  
    2.58 -	return (CMD_OK);
    2.59 +	return (retval);
    2.60  }
    2.61  
    2.62  static enum cmd_return
    2.63 @@ -338,7 +345,7 @@
    2.64  		}
    2.65  	}
    2.66  
    2.67 -	pager = pager_create(stdout);
    2.68 +	pager = pager_create(STDOUT_FILENO);
    2.69  	list = pwfile_create_list(ctx);
    2.70  	for (j = 0; list[j] != NULL; j++) {
    2.71  		if (list[j]->any.type == ITEM_TYPE_GROUP) {
    2.72 @@ -363,9 +370,7 @@
    2.73  			pwfile_destroy_record(record);
    2.74  		}
    2.75  	}
    2.76 -	pager_show(pager);
    2.77 -
    2.78 -	retval = CMD_OK;
    2.79 +	retval = (pager_show(pager) != IO_SIGNAL) ? CMD_OK : CMD_SIGNAL;
    2.80  
    2.81  out:
    2.82  	pager_destroy(pager);
    2.83 @@ -633,12 +638,11 @@
    2.84  			pwm_err(ctx, "record %u does not exist", id);
    2.85  			goto out;
    2.86  		}
    2.87 +		retval = CMD_OK;
    2.88  	} else {
    2.89 -		printf("%s\n", password);
    2.90 +		retval = io_printf("%s\n", password);
    2.91  	}
    2.92  
    2.93 -	retval = CMD_OK;
    2.94 -
    2.95  out:
    2.96  	free(char_groupv);
    2.97  
    2.98 @@ -667,14 +671,15 @@
    2.99  	return (CMD_OK);
   2.100  }
   2.101  
   2.102 -static void
   2.103 -print_record(struct record *record, int fields[], int show_labels, FILE *fp)
   2.104 +static int
   2.105 +print_record(struct record *record, int fields[], int show_labels, int fd)
   2.106  {
   2.107  	struct pager	*pager;
   2.108  	struct tm	*tm;
   2.109  	char		timebuf[TIME_SIZE];
   2.110 +	int		retval;
   2.111  
   2.112 -	pager = pager_create(fp);
   2.113 +	pager = pager_create(fd);
   2.114  	if (fields[FIELD_TITLE]) {
   2.115  		pager_printf(pager, "%s%s\n", show_labels ?
   2.116  		    field_labels[FIELD_TITLE] : "", (record->title != NULL) ?
   2.117 @@ -717,13 +722,16 @@
   2.118  		pager_printf(pager, "%s%s\n", show_labels ?
   2.119  		    field_labels[FIELD_MTIME] : "", timebuf);
   2.120  	}
   2.121 -	pager_show(pager);
   2.122 +	retval = pager_show(pager);
   2.123  	pager_destroy(pager);
   2.124 +
   2.125 +	return (retval);
   2.126  }
   2.127  
   2.128  static enum cmd_return
   2.129  cmd_show(struct pwm_ctx *ctx, int argc, char *argv[])
   2.130  {
   2.131 +	enum cmd_return	retval;
   2.132  	unsigned int	id;
   2.133  	struct record	*record;
   2.134  	int		i;
   2.135 @@ -767,10 +775,11 @@
   2.136  		pwm_err(ctx, "record %u does not exist", id);
   2.137  		return (CMD_ERR);
   2.138  	}
   2.139 -	print_record(record, fields, 1, stdout);
   2.140 +	retval = (print_record(record, fields, 1, STDOUT_FILENO) != IO_SIGNAL) ?
   2.141 +	    CMD_OK : CMD_SIGNAL;
   2.142  	pwfile_destroy_record(record);
   2.143  
   2.144 -	return (CMD_OK);
   2.145 +	return (retval);
   2.146  }
   2.147  
   2.148  static enum cmd_return
   2.149 @@ -781,7 +790,7 @@
   2.150  	struct record	*record = NULL;
   2.151  	enum field_type	type;
   2.152  	int		fields[COUNTOF(field_namev) - 1] = { 0 };
   2.153 -	FILE		*fp = NULL;
   2.154 +	struct proc	proc = { 0 };
   2.155  
   2.156  	if (argc != 4) {
   2.157  		return (CMD_USAGE);
   2.158 @@ -799,9 +808,7 @@
   2.159  	}
   2.160  	fields[type] = 1;
   2.161  
   2.162 -	fp = popen(argv[3], "w");
   2.163 -	if (fp == NULL) {
   2.164 -		warn("popen");
   2.165 +	if (proc_open(&proc, argv[3], "w") != IO_OK) {
   2.166  		goto out;
   2.167  	}
   2.168  
   2.169 @@ -811,14 +818,15 @@
   2.170  		goto out;
   2.171  	}
   2.172  
   2.173 -	print_record(record, fields, 0, fp);
   2.174 -
   2.175 -	retval = CMD_OK;
   2.176 +	retval = (print_record(record, fields, 0, proc.fd) != IO_SIGNAL) ?
   2.177 +	    CMD_OK : CMD_SIGNAL;
   2.178  
   2.179  out:
   2.180  	pwfile_destroy_record(record);
   2.181 -	if (fp != NULL) {
   2.182 -		pclose(fp);
   2.183 +	if (proc.pid != 0) {
   2.184 +		if (proc_close(&proc) == IO_SIGNAL) {
   2.185 +			retval = CMD_SIGNAL;
   2.186 +		}
   2.187  	}
   2.188  
   2.189  	return (retval);
   2.190 @@ -883,6 +891,7 @@
   2.191  static enum cmd_return
   2.192  cmd_help(struct pwm_ctx *ctx, int argc, char *argv[])
   2.193  {
   2.194 +	enum cmd_return	retval = CMD_OK;
   2.195  	struct pager	*pager;
   2.196  	struct cmd	*cmd;
   2.197  
   2.198 @@ -890,7 +899,7 @@
   2.199  		return (CMD_USAGE);
   2.200  	}
   2.201  
   2.202 -	pager = pager_create(stdout);
   2.203 +	pager = pager_create(STDOUT_FILENO);
   2.204  	if (argc == 2) {
   2.205  		for (cmd = cmds; cmd->cmd_func != NULL; cmd++) {
   2.206  			if ((strcmp(argv[1], cmd->abbrev_cmd) == 0) ||
   2.207 @@ -906,10 +915,10 @@
   2.208  			    cmd->full_cmd, cmd->description);
   2.209  		}
   2.210  	}
   2.211 -	pager_show(pager);
   2.212 +	retval = (pager_show(pager) != IO_SIGNAL) ? CMD_OK : CMD_SIGNAL;
   2.213  	pager_destroy(pager);
   2.214  
   2.215 -	return (CMD_OK);
   2.216 +	return (retval);
   2.217  }
   2.218  
   2.219  static enum cmd_return
     3.1 --- a/cmd.h	Thu Aug 24 13:10:56 2017 +0200
     3.2 +++ b/cmd.h	Fri Sep 01 22:33:41 2017 +0200
     3.3 @@ -31,6 +31,7 @@
     3.4  	CMD_STATUS,
     3.5  	CMD_ERR,
     3.6  	CMD_USAGE,
     3.7 +	CMD_SIGNAL,
     3.8  	CMD_QUIT
     3.9  };
    3.10  
     4.1 --- a/compat.h	Thu Aug 24 13:10:56 2017 +0200
     4.2 +++ b/compat.h	Fri Sep 01 22:33:41 2017 +0200
     4.3 @@ -31,6 +31,10 @@
     4.4  #include "compat/asprintf.h"
     4.5  #endif /* !HAVE_ASPRINTF */
     4.6  
     4.7 +#ifndef	HAVE_CLOSEFROM
     4.8 +#include "compat/closefrom.h"
     4.9 +#endif /* !HAVE_CLOSEFROM */
    4.10 +
    4.11  #ifndef	HAVE_ERR_H
    4.12  #include "compat/err.h"
    4.13  #endif /* !HAVE_ERR_H */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/compat/closefrom.c	Fri Sep 01 22:33:41 2017 +0200
     5.3 @@ -0,0 +1,46 @@
     5.4 +/*
     5.5 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
     5.6 + *
     5.7 + * Permission is hereby granted, free of charge, to any person obtaining
     5.8 + * a copy of this software and associated documentation files (the
     5.9 + * "Software"), to deal in the Software without restriction, including
    5.10 + * without limitation the rights to use, copy, modify, merge, publish,
    5.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    5.12 + * permit persons to whom the Software is furnished to do so, subject to
    5.13 + * the following conditions:
    5.14 + *
    5.15 + * The above copyright notice and this permission notice shall be included
    5.16 + * in all copies or substantial portions of the Software.
    5.17 + *
    5.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    5.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    5.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    5.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    5.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    5.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    5.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    5.25 + */
    5.26 +
    5.27 +#include <errno.h>
    5.28 +#include <fcntl.h>
    5.29 +#include <unistd.h>
    5.30 +
    5.31 +int
    5.32 +closefrom(int fd_min)
    5.33 +{
    5.34 +#ifdef	F_CLOSEM
    5.35 +	return fcntl(fd_min, F_CLOSEM, 0);
    5.36 +#else /* !F_CLOSEM */
    5.37 +	int	fd_max;
    5.38 +	int	fd;
    5.39 +
    5.40 +	fd_max = sysconf(_SC_OPEN_MAX);
    5.41 +	for (fd = fd_min; fd < fd_max; fd++) {
    5.42 +		if ((close(fd) < 0) && (errno != EBADF)) {
    5.43 +			return (-1);
    5.44 +		}
    5.45 +	}
    5.46 +
    5.47 +	return (0);
    5.48 +#endif /* !F_CLOSEM */
    5.49 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/compat/closefrom.h	Fri Sep 01 22:33:41 2017 +0200
     6.3 @@ -0,0 +1,29 @@
     6.4 +/*
     6.5 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
     6.6 + *
     6.7 + * Permission is hereby granted, free of charge, to any person obtaining
     6.8 + * a copy of this software and associated documentation files (the
     6.9 + * "Software"), to deal in the Software without restriction, including
    6.10 + * without limitation the rights to use, copy, modify, merge, publish,
    6.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    6.12 + * permit persons to whom the Software is furnished to do so, subject to
    6.13 + * the following conditions:
    6.14 + *
    6.15 + * The above copyright notice and this permission notice shall be included
    6.16 + * in all copies or substantial portions of the Software.
    6.17 + *
    6.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    6.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    6.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    6.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    6.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    6.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    6.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    6.25 + */
    6.26 +
    6.27 +#ifndef	CLOSEFROM_H
    6.28 +#define	CLOSEFROM_H
    6.29 +
    6.30 +int	closefrom(int);
    6.31 +
    6.32 +#endif /* !CLOSEFROM_H */
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/io.c	Fri Sep 01 22:33:41 2017 +0200
     7.3 @@ -0,0 +1,310 @@
     7.4 +/*
     7.5 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
     7.6 + *
     7.7 + * Permission is hereby granted, free of charge, to any person obtaining
     7.8 + * a copy of this software and associated documentation files (the
     7.9 + * "Software"), to deal in the Software without restriction, including
    7.10 + * without limitation the rights to use, copy, modify, merge, publish,
    7.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    7.12 + * permit persons to whom the Software is furnished to do so, subject to
    7.13 + * the following conditions:
    7.14 + *
    7.15 + * The above copyright notice and this permission notice shall be included
    7.16 + * in all copies or substantial portions of the Software.
    7.17 + *
    7.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    7.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    7.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    7.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    7.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    7.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    7.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    7.25 + */
    7.26 +
    7.27 +#include "compat.h"
    7.28 +
    7.29 +#ifdef	HAVE_ERR_H
    7.30 +#include <err.h>
    7.31 +#endif /* HAVE_ERR_H */
    7.32 +#include <errno.h>
    7.33 +#include <setjmp.h>
    7.34 +#include <signal.h>
    7.35 +#include <stdarg.h>
    7.36 +#include <stdio.h>
    7.37 +#include <stdio.h>
    7.38 +#include <string.h>
    7.39 +#include <unistd.h>
    7.40 +
    7.41 +#include "io.h"
    7.42 +#include "util.h"
    7.43 +#include "pwm.h"
    7.44 +
    7.45 +static sigjmp_buf	signal_env;
    7.46 +
    7.47 +static void
    7.48 +signal_handler(int signal_no)
    7.49 +{
    7.50 +	siglongjmp(signal_env, signal_no);
    7.51 +}
    7.52 +
    7.53 +int
    7.54 +io_gl_complete_nothing(WordCompletion *cpl, void *data, const char *line,
    7.55 +    int word_end)
    7.56 +{
    7.57 +	return (0);
    7.58 +}
    7.59 +
    7.60 +enum io_status
    7.61 +io_get_char(const char *prompt, int *cp)
    7.62 +{
    7.63 +	enum io_status	retval = IO_OK;
    7.64 +	GetLine	*gl = NULL;
    7.65 +
    7.66 +	gl = new_GetLine(16, 0);
    7.67 +	if (gl== NULL) {
    7.68 +		err(1, "new_GetLine");
    7.69 +	}
    7.70 +	gl_catch_blocked(gl);
    7.71 +
    7.72 +	/* prompt with echo off */
    7.73 +	gl_echo_mode(gl, 0);
    7.74 +	if ((*cp = gl_query_char(gl, prompt, '\0')) == EOF) {
    7.75 +		switch (gl_return_status(gl)) {
    7.76 +		case GLR_SIGNAL:
    7.77 +			retval = IO_SIGNAL;
    7.78 +			break;
    7.79 +		case GLR_ERROR:
    7.80 +			errx(1, "gl_get_line: %s",
    7.81 +			    gl_error_message(gl, NULL, 0));
    7.82 +		default:
    7.83 +			errx(1, "unknown error in gl_get_line");
    7.84 +		}
    7.85 +	}
    7.86 +
    7.87 +	/* erase prompt */
    7.88 +	if (io_printf("\r%*s\r", (int)strlen(prompt), "") == IO_SIGNAL) {
    7.89 +		retval = IO_SIGNAL;
    7.90 +	}
    7.91 +
    7.92 +	del_GetLine(gl);
    7.93 +
    7.94 +	return (retval);
    7.95 +}
    7.96 +
    7.97 +enum io_status
    7.98 +io_get_line(GetLine *gl, const char *prompt, int with_history,
    7.99 +    const char *start_line, int start_pos, size_t buf_size, char *buf)
   7.100 +{
   7.101 +	enum io_status	retval = IO_OK;
   7.102 +	GetLine		*gl_private = NULL;
   7.103 +	GlHistoryState	state;
   7.104 +	char		*line;
   7.105 +
   7.106 +	if (gl == NULL) {
   7.107 +		gl = gl_private = new_GetLine(buf_size - 1, 0);
   7.108 +		if (gl_private == NULL) {
   7.109 +			err(1, "new_GetLine");
   7.110 +		}
   7.111 +		gl_catch_blocked(gl_private);
   7.112 +	}
   7.113 +
   7.114 +	gl_state_of_history(gl, &state);
   7.115 +	gl_toggle_history(gl, with_history);
   7.116 +
   7.117 +	line = gl_get_line(gl, prompt, start_line, start_pos);
   7.118 +	if (line == NULL) {
   7.119 +		switch (gl_return_status(gl)) {
   7.120 +		case GLR_BLOCKED:
   7.121 +			break;
   7.122 +		case GLR_SIGNAL:
   7.123 +			retval = IO_SIGNAL;
   7.124 +			goto out;
   7.125 +		case GLR_EOF:
   7.126 +			retval = IO_EOF;
   7.127 +			goto out;
   7.128 +		case GLR_ERROR:
   7.129 +			errx(1, "gl_get_line: %s",
   7.130 +			    gl_error_message(gl, NULL, 0));
   7.131 +		default:
   7.132 +			errx(1, "unknown error in gl_get_line");
   7.133 +		}
   7.134 +	}
   7.135 +
   7.136 +	if (snprintf(buf, buf_size, "%s", line) >= (int)buf_size) {
   7.137 +		retval = IO_TRUNCATED;
   7.138 +	}
   7.139 +
   7.140 +out:
   7.141 +	if (gl != NULL) {
   7.142 +		gl_toggle_history(gl, state.enabled);
   7.143 +	}
   7.144 +	del_GetLine(gl_private);
   7.145 +
   7.146 +	return (retval);
   7.147 +}
   7.148 +
   7.149 +enum io_status
   7.150 +io_get_password(const char *prompt, const char *confirm_prompt,
   7.151 +    size_t buf_size, char *buf)
   7.152 +{
   7.153 +	enum io_status	retval = IO_OK;
   7.154 +	GetLine		*gl = NULL;
   7.155 +	size_t		len;
   7.156 +	char		*password_buf = NULL;
   7.157 +	char		*confirm_buf = NULL;
   7.158 +
   7.159 +	gl = new_GetLine(buf_size - 1, 0);
   7.160 +	if (gl == NULL) {
   7.161 +		err(1, "new_GetLine");
   7.162 +	}
   7.163 +	/* disable default filename completion */
   7.164 +	gl_customize_completion(gl, NULL, io_gl_complete_nothing);
   7.165 +	gl_echo_mode(gl, 0);
   7.166 +
   7.167 +	password_buf = xmalloc(buf_size);
   7.168 +
   7.169 +	if (io_get_line(gl, prompt, 0, NULL, 0, buf_size, password_buf) ==
   7.170 +	    IO_SIGNAL) {
   7.171 +		retval = IO_SIGNAL;
   7.172 +		goto out;
   7.173 +	}
   7.174 +	len = strlen(password_buf);
   7.175 +	/* strip trailing newline */
   7.176 +	if ((len > 0) && (password_buf[len - 1] == '\n')) {
   7.177 +		password_buf[--len] = '\0';
   7.178 +	}
   7.179 +	if (len == 0) {
   7.180 +		retval = IO_PASSWORD_EMPTY;
   7.181 +		goto out;
   7.182 +	}
   7.183 +
   7.184 +	if (confirm_prompt != NULL) {
   7.185 +		if (io_printf("\n") == IO_SIGNAL) {
   7.186 +			retval = IO_SIGNAL;
   7.187 +			goto out;
   7.188 +		}
   7.189 +
   7.190 +		/* confirm new password */
   7.191 +		confirm_buf = xmalloc(buf_size);
   7.192 +		if (io_get_line(gl, confirm_prompt, 0, NULL, 0,
   7.193 +		    buf_size, confirm_buf) == IO_SIGNAL) {
   7.194 +			retval = IO_SIGNAL;
   7.195 +			goto out;
   7.196 +		}
   7.197 +		len = strlen(confirm_buf);
   7.198 +		/* strip trailing newline */
   7.199 +		if ((len > 0) && (confirm_buf[len - 1] == '\n')) {
   7.200 +			confirm_buf[--len] = '\0';
   7.201 +		}
   7.202 +		if (strcmp(password_buf, confirm_buf) != 0) {
   7.203 +			retval = IO_PASSWORD_MISMATCH;
   7.204 +			goto out;
   7.205 +		}
   7.206 +	}
   7.207 +
   7.208 +	strcpy(buf, password_buf);
   7.209 +
   7.210 +out:
   7.211 +	if (io_printf("\n") == IO_SIGNAL) {
   7.212 +		retval = IO_SIGNAL;
   7.213 +		goto out;
   7.214 +	}
   7.215 +	free(password_buf);
   7.216 +	free(confirm_buf);
   7.217 +	del_GetLine(gl);
   7.218 +
   7.219 +	return (retval);
   7.220 +}
   7.221 +
   7.222 +enum io_status
   7.223 +io_dputs(int fd, const char *s)
   7.224 +{
   7.225 +	struct sigaction action;
   7.226 +	struct sigaction oaction;
   7.227 +	int		signal_no = 0;
   7.228 +	const char	*p = s;
   7.229 +	size_t		remaining;
   7.230 +	ssize_t		n;
   7.231 +
   7.232 +	/* install signal handlers */
   7.233 +	action.sa_handler = signal_handler;
   7.234 +	action.sa_flags = 0;
   7.235 +	sigemptyset(&action.sa_mask);
   7.236 +	sigaddset(&action.sa_mask, SIGINT);
   7.237 +	sigaddset(&action.sa_mask, SIGTERM);
   7.238 +	sigaddset(&action.sa_mask, SIGHUP);
   7.239 +	sigaddset(&action.sa_mask, SIGQUIT);
   7.240 +	if ((sigaction(SIGINT, &action, &oaction) != 0) ||
   7.241 +	    (sigaction(SIGTERM, &action, &oaction) != 0) ||
   7.242 +	    (sigaction(SIGHUP, &action, &oaction) != 0) ||
   7.243 +	    (sigaction(SIGQUIT, &action, &oaction) != 0)) {
   7.244 +		err(1, "sigaction");
   7.245 +	}
   7.246 +
   7.247 +	if ((signal_no = sigsetjmp(signal_env, 1)) != 0) {
   7.248 +		/* signal received, signal mask has been restored */
   7.249 +		goto out;
   7.250 +	}
   7.251 +
   7.252 +	remaining = strlen(s);
   7.253 +	while (remaining > 0) {
   7.254 +		pwm_unblock_signals();
   7.255 +		n = write(fd, p, remaining);
   7.256 +		if ((n < (int)remaining) && (errno != EINTR)) {
   7.257 +			err(1, "write");
   7.258 +		}
   7.259 +		pwm_block_signals();
   7.260 +		remaining -= MAX(n, 0);
   7.261 +		p += MAX(n, 0);
   7.262 +	}
   7.263 +
   7.264 +out:
   7.265 +	/* restore signal handlers */
   7.266 +	if ((sigaction(SIGINT, &oaction, NULL) != 0) ||
   7.267 +	    (sigaction(SIGTERM, &oaction, NULL) != 0) ||
   7.268 +	    (sigaction(SIGHUP, &oaction, NULL) != 0) ||
   7.269 +	    (sigaction(SIGQUIT, &oaction, NULL) != 0)) {
   7.270 +		err(1, "sigaction");
   7.271 +	}
   7.272 +
   7.273 +	return ((signal_no == 0) ? IO_OK : IO_SIGNAL);
   7.274 +}
   7.275 +
   7.276 +enum io_status
   7.277 +io_vdprintf(int fd, const char *fmt, va_list args)
   7.278 +{
   7.279 +	enum io_status	retval;
   7.280 +	char		*buf;
   7.281 +
   7.282 +	xvasprintf(&buf, fmt, args);
   7.283 +	retval = io_dputs(fd, buf);
   7.284 +	free(buf);
   7.285 +
   7.286 +	return (retval);
   7.287 +}
   7.288 +
   7.289 +enum io_status
   7.290 +io_dprintf(int fd, const char *fmt, ...)
   7.291 +{
   7.292 +	enum io_status	retval;
   7.293 +	va_list		args;
   7.294 +
   7.295 +	va_start(args, fmt);
   7.296 +	retval = io_vdprintf(fd, fmt, args);
   7.297 +	va_end(args);
   7.298 +
   7.299 +	return (retval);
   7.300 +}
   7.301 +
   7.302 +enum io_status
   7.303 +io_printf(const char *fmt, ...)
   7.304 +{
   7.305 +	enum io_status	retval;
   7.306 +	va_list		args;
   7.307 +
   7.308 +	va_start(args, fmt);
   7.309 +	retval = io_vdprintf(STDOUT_FILENO, fmt, args);
   7.310 +	va_end(args);
   7.311 +
   7.312 +	return (retval);
   7.313 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/io.h	Fri Sep 01 22:33:41 2017 +0200
     8.3 @@ -0,0 +1,51 @@
     8.4 +/*
     8.5 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
     8.6 + *
     8.7 + * Permission is hereby granted, free of charge, to any person obtaining
     8.8 + * a copy of this software and associated documentation files (the
     8.9 + * "Software"), to deal in the Software without restriction, including
    8.10 + * without limitation the rights to use, copy, modify, merge, publish,
    8.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    8.12 + * permit persons to whom the Software is furnished to do so, subject to
    8.13 + * the following conditions:
    8.14 + *
    8.15 + * The above copyright notice and this permission notice shall be included
    8.16 + * in all copies or substantial portions of the Software.
    8.17 + *
    8.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    8.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    8.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    8.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    8.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    8.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    8.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    8.25 + */
    8.26 +
    8.27 +#ifndef	IO_H
    8.28 +#define	IO_H
    8.29 +
    8.30 +#include <libtecla.h>
    8.31 +#include <stdarg.h>
    8.32 +
    8.33 +enum io_status {
    8.34 +	IO_OK,
    8.35 +	IO_EOF = -1,
    8.36 +	IO_ERROR = -2,
    8.37 +	IO_TRUNCATED = -3,
    8.38 +	IO_SIGNAL = -4,
    8.39 +	IO_PASSWORD_EMPTY = -5,
    8.40 +	IO_PASSWORD_MISMATCH = -6
    8.41 +};
    8.42 +
    8.43 +int		io_gl_complete_nothing(WordCompletion *, void *, const char *,
    8.44 +    int);
    8.45 +enum io_status	io_get_char(const char *, int *);
    8.46 +enum io_status	io_get_line(GetLine *, const char *, int, const char *, int,
    8.47 +    size_t, char *);
    8.48 +enum io_status	io_get_password(const char *, const char *, size_t, char *);
    8.49 +enum io_status	io_dputs(int, const char *);
    8.50 +enum io_status	io_vdprintf(int, const char *, va_list);
    8.51 +enum io_status	io_dprintf(int, const char *, ...);
    8.52 +enum io_status	io_printf(const char *, ...);
    8.53 +
    8.54 +#endif /* !IO_H */
     9.1 --- a/pager.c	Thu Aug 24 13:10:56 2017 +0200
     9.2 +++ b/pager.c	Fri Sep 01 22:33:41 2017 +0200
     9.3 @@ -28,7 +28,6 @@
     9.4  #endif /* HAVE_ERR_H */
     9.5  #include <errno.h>
     9.6  #include <fcntl.h>
     9.7 -#include <libtecla.h>
     9.8  #include <limits.h>
     9.9  #include <signal.h>
    9.10  #include <stdio.h>
    9.11 @@ -49,7 +48,7 @@
    9.12  
    9.13  struct pager {
    9.14  	TAILQ_HEAD(lines_head, lines_entry) lines_head;
    9.15 -	FILE		*fp;
    9.16 +	int		fd;
    9.17  	size_t		buf_size;
    9.18  	char		*buf;
    9.19  };
    9.20 @@ -59,32 +58,14 @@
    9.21  	char		*line;
    9.22  };
    9.23  
    9.24 -static int
    9.25 -getch_prompt(GetLine *gl, const char *prompt)
    9.26 -{
    9.27 -	int	c;
    9.28 -	int	saved_echo_mode;
    9.29 -
    9.30 -	/* prompt with echo off */
    9.31 -	saved_echo_mode = gl_echo_mode(gl, -1);
    9.32 -	gl_echo_mode(gl, 0);
    9.33 -	c = gl_query_char(gl, prompt, '\0');
    9.34 -	gl_echo_mode(gl, saved_echo_mode);
    9.35 -
    9.36 -	/* erase prompt */
    9.37 -	printf("\r%*s\r", (int)strlen(prompt), "");
    9.38 -
    9.39 -	return (c);
    9.40 -}
    9.41 -
    9.42  struct pager *
    9.43 -pager_create(FILE *fp)
    9.44 +pager_create(int fd)
    9.45  {
    9.46  	struct pager	*pager;
    9.47  
    9.48  	pager = xmalloc(sizeof (struct pager));
    9.49  	TAILQ_INIT(&pager->lines_head);
    9.50 -	pager->fp = fp;
    9.51 +	pager->fd = fd;
    9.52  	pager->buf_size = BUFSIZ;
    9.53  	pager->buf = xmalloc(BUFSIZ);
    9.54  
    9.55 @@ -183,9 +164,10 @@
    9.56  	return (len);
    9.57  }
    9.58  
    9.59 -static unsigned int
    9.60 -mbsnprint(unsigned int cols, const char *s, FILE *fp)
    9.61 +static int
    9.62 +mbsnprint(unsigned int cols, const char *s, int fd)
    9.63  {
    9.64 +	int		retval;
    9.65  	const char	*p = s;
    9.66  	unsigned int	col = 0;
    9.67  	int		mb_len;
    9.68 @@ -232,21 +214,22 @@
    9.69  		p += mb_len;
    9.70  		col += width;
    9.71  		if (col <= cols) {
    9.72 -			if (fputs(mb_buf, fp) == EOF) {
    9.73 -				err(1, "fputs");
    9.74 +			retval = io_dputs(fd, mb_buf);
    9.75 +			if (retval != IO_OK) {
    9.76 +				return (retval);
    9.77  			}
    9.78  		}
    9.79  	}
    9.80  
    9.81 -	fputc('\n', fp);
    9.82 -	fflush(fp);
    9.83 +	retval = io_dputs(fd, "\n");
    9.84  
    9.85 -	return (col);
    9.86 +	return (retval);
    9.87  }
    9.88  
    9.89 -void
    9.90 +enum io_status
    9.91  pager_show(struct pager *pager)
    9.92  {
    9.93 +	int		retval = IO_OK;
    9.94  	int		is_interactive;
    9.95  	unsigned int	rows = 24;
    9.96  	unsigned int	cols = 80;
    9.97 @@ -254,40 +237,45 @@
    9.98  	struct winsize	ws;
    9.99  #endif /* TIOCGWINSZ */
   9.100  	unsigned int	row = 0;
   9.101 -	GetLine		*gl;
   9.102  	struct lines_entry *entry;
   9.103 +	int		c;
   9.104  
   9.105 -	is_interactive = (isatty(STDIN_FILENO) && (pager->fp == stdout));
   9.106 +	is_interactive = (isatty(STDIN_FILENO) && (pager->fd == STDOUT_FILENO));
   9.107  
   9.108  #ifdef	TIOCGWINSZ
   9.109  	if (is_interactive) {
   9.110  		/* determine terminal size */
   9.111 -		if (ioctl(fileno(pager->fp), TIOCGWINSZ, &ws) == 0) {
   9.112 +		if (ioctl(pager->fd, TIOCGWINSZ, &ws) == 0) {
   9.113  			rows = (ws.ws_row > 0) ? ws.ws_row : rows;
   9.114  			cols = (ws.ws_col > 0) ? ws.ws_col : cols;
   9.115  		}
   9.116  	}
   9.117  #endif /* TIOCGWINSZ */
   9.118  
   9.119 -	gl = new_GetLine(10, 0);
   9.120 -	if (gl == NULL) {
   9.121 -		err(1, "new_GetLine");
   9.122 -	}
   9.123 -
   9.124  	TAILQ_FOREACH(entry, &pager->lines_head, entry) {
   9.125  		if (is_interactive) {
   9.126 -			mbsnprint(cols, entry->line, pager->fp);
   9.127 +			retval = mbsnprint(cols, entry->line, pager->fd);
   9.128 +			if (retval != IO_OK) {
   9.129 +				goto out;
   9.130 +			}
   9.131  			row++;
   9.132  			if ((TAILQ_NEXT(entry, entry) != NULL) &&
   9.133  			    (row == rows - 1)) {
   9.134 -				getch_prompt(gl, "--More--");
   9.135 +				/* prompt for keypress */
   9.136 +				retval = io_get_char("--More--", &c);
   9.137 +				if (retval != IO_OK) {
   9.138 +					goto out;
   9.139 +				}
   9.140  				row = 0;
   9.141  			}
   9.142  		} else {
   9.143 -			fprintf(pager->fp, "%s", entry->line);
   9.144 -			fflush(pager->fp);
   9.145 +			retval = io_dputs(pager->fd, entry->line);
   9.146 +			if (retval != IO_OK) {
   9.147 +				goto out;
   9.148 +			}
   9.149  		}
   9.150  	}
   9.151  
   9.152 -	del_GetLine(gl);
   9.153 +out:
   9.154 +	return (retval);
   9.155  }
    10.1 --- a/pager.h	Thu Aug 24 13:10:56 2017 +0200
    10.2 +++ b/pager.h	Fri Sep 01 22:33:41 2017 +0200
    10.3 @@ -27,12 +27,14 @@
    10.4  
    10.5  #include <stdarg.h>
    10.6  
    10.7 +#include "io.h"
    10.8 +
    10.9  struct pager;
   10.10  
   10.11 -struct pager *	pager_create(FILE *fp);
   10.12 +struct pager *	pager_create(int);
   10.13  void		pager_destroy(struct pager *);
   10.14  int		pager_vprintf(struct pager *, const char *, va_list);
   10.15  int		pager_printf(struct pager *, const char *, ...);
   10.16 -void		pager_show(struct pager *);
   10.17 +enum io_status	pager_show(struct pager *);
   10.18  
   10.19  #endif /* !PAGER_H */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/proc.c	Fri Sep 01 22:33:41 2017 +0200
    11.3 @@ -0,0 +1,161 @@
    11.4 +/*
    11.5 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
    11.6 + *
    11.7 + * Permission is hereby granted, free of charge, to any person obtaining
    11.8 + * a copy of this software and associated documentation files (the
    11.9 + * "Software"), to deal in the Software without restriction, including
   11.10 + * without limitation the rights to use, copy, modify, merge, publish,
   11.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   11.12 + * permit persons to whom the Software is furnished to do so, subject to
   11.13 + * the following conditions:
   11.14 + *
   11.15 + * The above copyright notice and this permission notice shall be included
   11.16 + * in all copies or substantial portions of the Software.
   11.17 + *
   11.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   11.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   11.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   11.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   11.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   11.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   11.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   11.25 + */
   11.26 +
   11.27 +#include "compat.h"
   11.28 +
   11.29 +#ifdef	HAVE_ERR_H
   11.30 +#include <err.h>
   11.31 +#endif /* HAVE_ERR_H */
   11.32 +#include <errno.h>
   11.33 +#include <setjmp.h>
   11.34 +#include <signal.h>
   11.35 +#include <string.h>
   11.36 +#include <sys/wait.h>
   11.37 +#include <unistd.h>
   11.38 +
   11.39 +#include "util.h"
   11.40 +#include "proc.h"
   11.41 +#include "pwm.h"
   11.42 +
   11.43 +#define	PIPE_R	0
   11.44 +#define	PIPE_W	1
   11.45 +
   11.46 +static sigjmp_buf	signal_env;
   11.47 +
   11.48 +static void
   11.49 +signal_handler(int signal_no)
   11.50 +{
   11.51 +	siglongjmp(signal_env, signal_no);
   11.52 +}
   11.53 +
   11.54 +enum io_status
   11.55 +proc_open(struct proc *proc, const char *command, const char *type)
   11.56 +{
   11.57 +	int	pipe_fds[2];
   11.58 +	pid_t	pid;
   11.59 +
   11.60 +	if ((strlen(type) != 1) || ((*type != 'r') && (*type != 'w'))) {
   11.61 +		return (IO_ERROR);
   11.62 +	}
   11.63 +
   11.64 +	if (pipe(pipe_fds) < 0) {
   11.65 +		return (IO_ERROR);
   11.66 +	}
   11.67 +
   11.68 +	switch (pid = fork()) {
   11.69 +	case -1:
   11.70 +		return (IO_ERROR);
   11.71 +	case 0:
   11.72 +		if (*type == 'r') {
   11.73 +			close(pipe_fds[PIPE_R]);
   11.74 +			dup2(pipe_fds[PIPE_W], STDOUT_FILENO);
   11.75 +		} else {
   11.76 +			close(pipe_fds[PIPE_W]);
   11.77 +			dup2(pipe_fds[PIPE_R], STDIN_FILENO);
   11.78 +		}
   11.79 +		closefrom(STDERR_FILENO + 1);
   11.80 +		execlp("sh", "sh", "-c", command, (char *)0);
   11.81 +		err(1, "execlp");
   11.82 +	default:
   11.83 +		if (*type == 'r') {
   11.84 +			close(pipe_fds[PIPE_W]);
   11.85 +			proc->fd = pipe_fds[PIPE_R];
   11.86 +		} else {
   11.87 +			close(pipe_fds[PIPE_R]);
   11.88 +			proc->fd = pipe_fds[PIPE_W];
   11.89 +		}
   11.90 +		proc->pid = pid;
   11.91 +		return (IO_OK);
   11.92 +	}
   11.93 +}
   11.94 +
   11.95 +enum io_status
   11.96 +proc_close(struct proc *proc)
   11.97 +{
   11.98 +	struct sigaction action;
   11.99 +	struct sigaction oaction;
  11.100 +	int		signal_no = 0;
  11.101 +	pid_t		wpid;
  11.102 +	int		status;
  11.103 +
  11.104 +	close(proc->fd);
  11.105 +
  11.106 +	/* install signal handlers */
  11.107 +	action.sa_handler = signal_handler;
  11.108 +	action.sa_flags = 0;
  11.109 +	sigemptyset(&action.sa_mask);
  11.110 +	sigaddset(&action.sa_mask, SIGINT);
  11.111 +	sigaddset(&action.sa_mask, SIGTERM);
  11.112 +	sigaddset(&action.sa_mask, SIGHUP);
  11.113 +	sigaddset(&action.sa_mask, SIGQUIT);
  11.114 +	if ((sigaction(SIGINT, &action, &oaction) != 0) ||
  11.115 +	    (sigaction(SIGTERM, &action, &oaction) != 0) ||
  11.116 +	    (sigaction(SIGHUP, &action, &oaction) != 0) ||
  11.117 +	    (sigaction(SIGQUIT, &action, &oaction) != 0)) {
  11.118 +		err(1, "sigaction");
  11.119 +	}
  11.120 +
  11.121 +	if ((signal_no = sigsetjmp(signal_env, 1)) != 0) {
  11.122 +		/*
  11.123 +		 * signal received, signal mask has been restored, send SIGTERM
  11.124 +		 * to the child process, wait 500 ms and send a SIGKILL if the
  11.125 +		 * child still exists
  11.126 +		 */
  11.127 +		kill(proc->pid, SIGTERM);
  11.128 +		nanosleep(&(struct timespec){ .tv_nsec = 500 * 1000 * 1000 },
  11.129 +		    NULL);
  11.130 +		do {
  11.131 +			wpid = waitpid(proc->pid, &status, WNOHANG);
  11.132 +		} while ((wpid == -1) && (errno == EINTR));
  11.133 +		if (wpid == proc->pid) {
  11.134 +			goto out;
  11.135 +		}
  11.136 +
  11.137 +		kill(proc->pid, SIGKILL);
  11.138 +		do {
  11.139 +			wpid = waitpid(proc->pid, &status, 0);
  11.140 +		} while ((wpid == -1) && (errno == EINTR));
  11.141 +
  11.142 +		goto out;
  11.143 +	}
  11.144 +
  11.145 +	pwm_unblock_signals();
  11.146 +	do {
  11.147 +		wpid = waitpid(proc->pid, &status, 0);
  11.148 +	} while ((wpid == -1) && (errno == EINTR));
  11.149 +	pwm_block_signals();
  11.150 +
  11.151 +out:
  11.152 +	/* restore signal handlers */
  11.153 +	if ((sigaction(SIGINT, &oaction, NULL) != 0) ||
  11.154 +	    (sigaction(SIGTERM, &oaction, NULL) != 0) ||
  11.155 +	    (sigaction(SIGHUP, &oaction, NULL) != 0) ||
  11.156 +	    (sigaction(SIGQUIT, &oaction, NULL) != 0)) {
  11.157 +		err(1, "sigaction");
  11.158 +	}
  11.159 +
  11.160 +	proc->fd = -1;
  11.161 +	proc->pid = (pid_t)-1;
  11.162 +
  11.163 +	return ((signal_no == 0) ? IO_OK : IO_SIGNAL);
  11.164 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/proc.h	Fri Sep 01 22:33:41 2017 +0200
    12.3 @@ -0,0 +1,37 @@
    12.4 +/*
    12.5 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
    12.6 + *
    12.7 + * Permission is hereby granted, free of charge, to any person obtaining
    12.8 + * a copy of this software and associated documentation files (the
    12.9 + * "Software"), to deal in the Software without restriction, including
   12.10 + * without limitation the rights to use, copy, modify, merge, publish,
   12.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   12.12 + * permit persons to whom the Software is furnished to do so, subject to
   12.13 + * the following conditions:
   12.14 + *
   12.15 + * The above copyright notice and this permission notice shall be included
   12.16 + * in all copies or substantial portions of the Software.
   12.17 + *
   12.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   12.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   12.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   12.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   12.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   12.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   12.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   12.25 + */
   12.26 +
   12.27 +#ifndef	PROC_H
   12.28 +#define	PROC_H
   12.29 +
   12.30 +#include "io.h"
   12.31 +
   12.32 +struct proc {
   12.33 +	int	fd;
   12.34 +	pid_t	pid;
   12.35 +};
   12.36 +
   12.37 +enum io_status	proc_open(struct proc *, const char *, const char *);
   12.38 +enum io_status	proc_close(struct proc *);
   12.39 +
   12.40 +#endif /* !PROC_H */
    13.1 --- a/pwm.c	Thu Aug 24 13:10:56 2017 +0200
    13.2 +++ b/pwm.c	Fri Sep 01 22:33:41 2017 +0200
    13.3 @@ -41,22 +41,11 @@
    13.4  
    13.5  #include "pwm.h"
    13.6  #include "cmd.h"
    13.7 +#include "io.h"
    13.8  #include "pwfile.h"
    13.9  #include "tok.h"
   13.10  #include "util.h"
   13.11  
   13.12 -#ifndef	PWM_HISTORY_ENTRIES_MAX
   13.13 -#define	PWM_HISTORY_ENTRIES_MAX	1024
   13.14 -#endif /* !PWM_HISTORY_MAX */
   13.15 -
   13.16 -#ifndef	PWM_HISTORY_LINES_MAX
   13.17 -#define	PWM_HISTORY_LINES_MAX	256
   13.18 -#endif /* !PWM_HISTORY_LINES_MAX */
   13.19 -
   13.20 -#ifndef	PWM_HISTORY_MAX
   13.21 -#define	PWM_HISTORY_MAX	(PWM_HISTORY_LINES_MAX * PWM_LINE_MAX)
   13.22 -#endif /* !PWM_HISTORY_MAX */
   13.23 -
   13.24  static void
   13.25  usage(void)
   13.26  {
   13.27 @@ -81,11 +70,34 @@
   13.28  	}
   13.29  }
   13.30  
   13.31 -static int
   13.32 -complete_nothing(WordCompletion *cpl, void *data, const char *line,
   13.33 -    int word_end)
   13.34 +void
   13.35 +pwm_block_signals(void)
   13.36  {
   13.37 -	return (0);
   13.38 +	sigset_t	set;
   13.39 +
   13.40 +	sigemptyset(&set);
   13.41 +	sigaddset(&set, SIGINT);
   13.42 +	sigaddset(&set, SIGTERM);
   13.43 +	sigaddset(&set, SIGHUP);
   13.44 +	sigaddset(&set, SIGQUIT);
   13.45 +	if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) {
   13.46 +		err(1, "sigprocmask");
   13.47 +	}
   13.48 +}
   13.49 +
   13.50 +void
   13.51 +pwm_unblock_signals(void)
   13.52 +{
   13.53 +	sigset_t	set;
   13.54 +
   13.55 +	sigemptyset(&set);
   13.56 +	sigaddset(&set, SIGINT);
   13.57 +	sigaddset(&set, SIGTERM);
   13.58 +	sigaddset(&set, SIGHUP);
   13.59 +	sigaddset(&set, SIGQUIT);
   13.60 +	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
   13.61 +		err(1, "sigprocmask");
   13.62 +	}
   13.63  }
   13.64  
   13.65  static int
   13.66 @@ -94,9 +106,8 @@
   13.67  	int		retval = -1;
   13.68  	char		prompt[8 + 2 + 1];
   13.69  	GetLine		*gl = NULL;
   13.70 -	char		buf[PWM_LINE_MAX];
   13.71 -	char		*line = buf;
   13.72 -	int		c;
   13.73 +	char		buf[PWM_LINE_MAX + 1];
   13.74 +	int		io_retval;
   13.75  	int		argc = 0;
   13.76  	char		**argv = NULL;
   13.77  	struct cmd	*cmd;
   13.78 @@ -104,31 +115,41 @@
   13.79  
   13.80  	snprintf(prompt, sizeof (prompt), "%.*s> ", 8, getprogname());
   13.81  
   13.82 +	pwm_block_signals();
   13.83 +
   13.84  	/* initialize libtecla */
   13.85  	gl = new_GetLine(PWM_LINE_MAX, PWM_HISTORY_MAX);
   13.86  	if (gl == NULL) {
   13.87  		err(1, "new_GetLine");
   13.88  	}
   13.89 +	gl_catch_blocked(gl);
   13.90  	gl_limit_history(gl, PWM_HISTORY_LINES_MAX);
   13.91  	/* disable default filename completion */
   13.92 -	gl_customize_completion(gl, NULL, complete_nothing);
   13.93 +	gl_customize_completion(gl, NULL, io_gl_complete_nothing);
   13.94  
   13.95  	for (;;) {
   13.96 +		/* read next line */
   13.97  		cmd = NULL;
   13.98 -		line = gl_get_line(gl, prompt, NULL, -1);
   13.99 -		if (line == NULL) {
  13.100 -			switch (gl_return_status(gl)) {
  13.101 -			case GLR_EOF:
  13.102 -				break;
  13.103 -			case GLR_ERROR:
  13.104 -				warnx("gl_get_line: %s",
  13.105 -				    gl_error_message(gl, NULL, 0));
  13.106 -			}
  13.107 +		buf[0] = '\0';
  13.108 +		io_retval = io_get_line(gl, prompt, 1, NULL, 0,
  13.109 +		    sizeof (buf), buf);
  13.110 +		switch (io_retval) {
  13.111 +		case IO_OK:
  13.112  			break;
  13.113 +		case IO_TRUNCATED:
  13.114 +			/* line was truncated in non-interactive mode */
  13.115 +			fprintf(stderr, "line too long\n");
  13.116 +			goto out;
  13.117 +		case IO_EOF:	/* FALLTHROUGH */
  13.118 +		case IO_SIGNAL:
  13.119 +			goto quit;
  13.120 +		default:
  13.121 +			fprintf(stderr, "unknown error\n");
  13.122 +			goto quit;
  13.123  		}
  13.124  
  13.125  		/* tokenize line */
  13.126 -		switch (tok_tokenize(line, &argc, &argv)) {
  13.127 +		switch (tok_tokenize(buf, &argc, &argv)) {
  13.128  		case TOK_ERR_SYSTEM_ERROR:
  13.129  			err(1, "tok_tokenize");
  13.130  		case TOK_ERR_UNTERMINATED_QUOTE:
  13.131 @@ -171,7 +192,9 @@
  13.132  				goto out;
  13.133  			}
  13.134  			break;
  13.135 -		case CMD_QUIT:
  13.136 +		case CMD_SIGNAL:
  13.137 +			fprintf(stderr, "received signal, quitting\n");
  13.138 +		case CMD_QUIT:	/* FALLTHROUGH */
  13.139  			goto quit;
  13.140  		}
  13.141  		ctx->prev_cmd = cmd->full_cmd;
  13.142 @@ -201,69 +224,22 @@
  13.143  int
  13.144  pwm_read_password(struct pwm_ctx *ctx, int is_new_password)
  13.145  {
  13.146 -	int	retval = -1;
  13.147 -	GetLine	*gl = NULL;
  13.148 -	char	*line;
  13.149 -	size_t	len;
  13.150 -	char	password_buf[sizeof (ctx->password)] = { '\0' };
  13.151 -
  13.152 -	/* initialize libtecla */
  13.153 -	gl = new_GetLine(sizeof (password_buf) - 1, 0);
  13.154 -	if (gl == NULL) {
  13.155 -		err(1, "new_GetLine");
  13.156 +	switch (io_get_password(is_new_password ? "New Password:" :
  13.157 +	    "Password:", is_new_password ? "Confirm Password:" : NULL,
  13.158 +	    sizeof (ctx->password), ctx->password)) {
  13.159 +	case IO_OK:
  13.160 +		return (0);
  13.161 +	case IO_SIGNAL:
  13.162 +		return (-2);
  13.163 +	case IO_PASSWORD_EMPTY:
  13.164 +		pwm_err(ctx, "password must not be empty");
  13.165 +		return (-1);
  13.166 +	case IO_PASSWORD_MISMATCH:
  13.167 +		pwm_err(ctx, "passwords do not match");
  13.168 +		return (-1);
  13.169 +	default:
  13.170 +		return (-1);
  13.171  	}
  13.172 -	/* disable default filename completion */
  13.173 -	gl_customize_completion(gl, NULL, complete_nothing);
  13.174 -	gl_echo_mode(gl, 0);
  13.175 -
  13.176 -	line = gl_get_line(gl, is_new_password ? "New password: " :
  13.177 -	    "Password: ", NULL, -1);
  13.178 -	putchar('\n');
  13.179 -	if (line == NULL) {
  13.180 -		if (gl_return_status(gl) == GLR_ERROR) {
  13.181 -			errx(1, "gl_get_line: %s", gl_error_message(gl, NULL,
  13.182 -			    0));
  13.183 -		}
  13.184 -		goto out;
  13.185 -	}
  13.186 -	len = strlen(line);
  13.187 -	if ((len > 0) && (line[len - 1] == '\n')) {
  13.188 -		line[--len] = '\0';
  13.189 -	}
  13.190 -	if (len == 0) {
  13.191 -		fprintf(stderr, "password must not be empty\n");
  13.192 -		goto out;
  13.193 -	}
  13.194 -	strcpy(password_buf, line);
  13.195 -
  13.196 -	/* confirm the entered password entered */
  13.197 -	if (is_new_password) {
  13.198 -		line = gl_get_line(gl, "Confirm password: ", NULL, -1);
  13.199 -		putchar('\n');
  13.200 -		if (line == NULL) {
  13.201 -			if (gl_return_status(gl) == GLR_ERROR) {
  13.202 -				errx(1, "gl_get_line: %s", gl_error_message(gl,
  13.203 -				    NULL, 0));
  13.204 -			}
  13.205 -			goto out;
  13.206 -		}
  13.207 -		len = strlen(line);
  13.208 -		if ((len > 0) && (line[len - 1] == '\n')) {
  13.209 -			line[--len] = '\0';
  13.210 -		}
  13.211 -		if (strcmp(password_buf, line) != 0) {
  13.212 -			fprintf(stderr, "passwords do not match\n");
  13.213 -			goto out;
  13.214 -		}
  13.215 -	}
  13.216 -
  13.217 -	strcpy(ctx->password, password_buf);
  13.218 -	retval = 0;
  13.219 -
  13.220 -out:
  13.221 -	del_GetLine(gl);
  13.222 -
  13.223 -	return (retval);
  13.224  }
  13.225  
  13.226  static int
    14.1 --- a/pwm.h	Thu Aug 24 13:10:56 2017 +0200
    14.2 +++ b/pwm.h	Fri Sep 01 22:33:41 2017 +0200
    14.3 @@ -31,6 +31,18 @@
    14.4  #define	PWM_LINE_MAX	16384
    14.5  #endif /* !PWM_LINE_MAX */
    14.6  
    14.7 +#ifndef	PWM_HISTORY_ENTRIES_MAX
    14.8 +#define	PWM_HISTORY_ENTRIES_MAX	1024
    14.9 +#endif /* !PWM_HISTORY_MAX */
   14.10 +
   14.11 +#ifndef	PWM_HISTORY_LINES_MAX
   14.12 +#define	PWM_HISTORY_LINES_MAX	256
   14.13 +#endif /* !PWM_HISTORY_LINES_MAX */
   14.14 +
   14.15 +#ifndef	PWM_HISTORY_MAX
   14.16 +#define	PWM_HISTORY_MAX	(PWM_HISTORY_LINES_MAX * PWM_LINE_MAX)
   14.17 +#endif /* !PWM_HISTORY_MAX */
   14.18 +
   14.19  struct pwm_ctx {
   14.20  	const char	*prev_cmd;
   14.21  	char		*errmsg;
   14.22 @@ -43,6 +55,8 @@
   14.23  };
   14.24  
   14.25  void	pwm_err(struct pwm_ctx *, char *, ...);
   14.26 +void	pwm_block_signals(void);
   14.27 +void	pwm_unblock_signals(void);
   14.28  int	pwm_read_password(struct pwm_ctx *, int);
   14.29  
   14.30  #endif /* PWM_H */