tools/llvm: Do not build with symbols
[minix3.git] / lib / libc / gen / getpass.c
blob8f026968c0829a1b9a683230928f4620f6039955
1 /* $NetBSD: getpass.c,v 1.27 2012/05/26 19:34:16 christos Exp $ */
3 /*-
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
32 #if defined(LIBC_SCCS) && !defined(lint)
33 __RCSID("$NetBSD: getpass.c,v 1.27 2012/05/26 19:34:16 christos Exp $");
34 #endif /* LIBC_SCCS and not lint */
36 #include "namespace.h"
38 #include <assert.h>
39 #ifdef TEST
40 #include <stdio.h>
41 #endif
42 #include <errno.h>
43 #include <ctype.h>
44 #include <signal.h>
45 #include <string.h>
46 #include <paths.h>
47 #include <stdbool.h>
48 #include <stdlib.h>
49 #include <termios.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <poll.h>
54 #ifdef __weak_alias
55 __weak_alias(getpassfd,_getpassfd)
56 __weak_alias(getpass_r,_getpass_r)
57 __weak_alias(getpass,_getpass)
58 #endif
61 * Notes:
62 * - There is no getpass_r in POSIX
63 * - Historically EOF is documented to be treated as EOL, we provide a
64 * tunable for that GETPASS_FAIL_EOF to disable this.
65 * - Historically getpass ate extra characters silently, we provide
66 * a tunable for that GETPASS_BUF_LIMIT to disable this.
67 * - Historically getpass "worked" by echoing characters when turning
68 * off echo failed, we provide a tunable GETPASS_NEED_TTY to
69 * disable this.
70 * - Some implementations say that on interrupt the program shall
71 * receive an interrupt signal before the function returns. We
72 * send all the tty signals before we return, but we don't expect
73 * suspend to do something useful unless the caller calls us again.
74 * We also provide a tunable to disable signal delivery
75 * GETPASS_NO_SIGNAL.
76 * - GETPASS_NO_BEEP disables beeping.
77 * - GETPASS_ECHO_STAR will echo '*' for each character of the password
78 * - GETPASS_ECHO will echo the password (as pam likes it)
79 * - GETPASS_7BIT strips the 8th bit
80 * - GETPASS_FORCE_UPPER forces to uppercase
81 * - GETPASS_FORCE_LOWER forces to uppercase
82 * - GETPASS_ECHO_NL echo's a new line on success if echo was off.
84 char *
85 /*ARGSUSED*/
86 getpassfd(const char *prompt, char *buf, size_t len, int *fd, int flags,
87 int tout)
89 struct termios gt;
90 char c;
91 int sig;
92 bool lnext, havetty, allocated, opentty, good;
93 int fdc[3];
95 _DIAGASSERT(prompt != NULL);
97 if (buf != NULL && len == 0) {
98 errno = EINVAL;
99 return NULL;
102 good = false;
103 opentty = false;
104 if (fd == NULL) {
106 * Try to use /dev/tty if possible; otherwise read from stdin
107 * and write to stderr.
109 fd = fdc;
110 if ((fd[0] = fd[1] = fd[2] = open(_PATH_TTY, O_RDWR)) == -1) {
111 fd[0] = STDIN_FILENO;
112 fd[1] = fd[2] = STDERR_FILENO;
113 } else
114 opentty = true;
117 sig = 0;
118 allocated = buf == NULL;
119 if (tcgetattr(fd[0], &gt) == -1) {
120 havetty = false;
121 if (flags & GETPASS_NEED_TTY)
122 goto out;
123 memset(&gt, -1, sizeof(gt));
124 } else
125 havetty = true;
127 if (havetty) {
128 struct termios st = gt;
130 st.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL|ISIG|ICANON);
131 st.c_cc[VMIN] = 1;
132 st.c_cc[VTIME] = 0;
133 if (tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &st) == -1)
134 goto out;
137 if (prompt != NULL) {
138 size_t plen = strlen(prompt);
139 (void)write(fd[1], prompt, plen);
142 if (allocated) {
143 len = 1024;
144 if ((buf = malloc(len)) == NULL)
145 goto restore;
148 c = '\1';
149 lnext = false;
150 for (size_t l = 0; c != '\0'; ) {
151 if (tout) {
152 struct pollfd pfd;
153 pfd.fd = fd[0];
154 pfd.events = POLLIN|POLLRDNORM;
155 pfd.revents = 0;
156 switch (poll(&pfd, 1, tout * 1000)) {
157 case 0:
158 errno = ETIMEDOUT;
159 /*FALLTHROUGH*/
160 case -1:
161 goto restore;
162 default:
163 break;
166 if (read(fd[0], &c, 1) != 1)
167 goto restore;
169 #define beep() \
170 do \
171 if (flags & GETPASS_NO_BEEP) \
172 (void)write(fd[2], "\a", 1); \
173 while (/*CONSTCOND*/ 0)
174 #define erase() (void)write(fd[1], "\b \b", 3)
176 * We test for both _POSIX_VDISABLE and NUL here because _POSIX_VDISABLE
177 * propagation does not seem to be very consistent on multiple daemon hops
178 * between different OS's. Perhaps we should not even bother with
179 * _POSIX_VDISABLE and use ~0 and 0 directly.
181 #define C(a, b) ((gt.c_cc[(a)] == _POSIX_VDISABLE || gt.c_cc[(a)] == '\0') ? \
182 (b) : gt.c_cc[(a)])
183 if (lnext) {
184 lnext = false;
185 goto add;
188 /* Ignored */
189 if (c == C(VREPRINT, CTRL('r')) || c == C(VSTART, CTRL('q')) ||
190 c == C(VSTOP, CTRL('s')) || c == C(VSTATUS, CTRL('t')) ||
191 c == C(VDISCARD, CTRL('o')))
192 continue;
194 /* Literal next */
195 if (c == C(VLNEXT, CTRL('v'))) {
196 lnext = true;
197 continue;
200 /* Line or word kill, treat as reset */
201 if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) {
202 if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR)) {
203 while (l--)
204 erase();
206 l = 0;
207 continue;
210 /* Character erase */
211 if (c == C(VERASE, CTRL('h'))) {
212 if (l == 0)
213 beep();
214 else {
215 l--;
216 if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR))
217 erase();
219 continue;
222 /* tty signal characters */
223 if (c == C(VINTR, CTRL('c'))) {
224 sig = SIGINT;
225 goto out;
227 if (c == C(VQUIT, CTRL('\\'))) {
228 sig = SIGQUIT;
229 goto out;
231 if (c == C(VSUSP, CTRL('z')) || c == C(VDSUSP, CTRL('y'))) {
232 sig = SIGTSTP;
233 goto out;
236 /* EOF */
237 if (c == C(VEOF, CTRL('d'))) {
238 if (flags & GETPASS_FAIL_EOF) {
239 errno = ENODATA;
240 goto out;
241 } else {
242 c = '\0';
243 goto add;
247 /* End of line */
248 if (c == C(VEOL, CTRL('j')) || c == C(VEOL2, CTRL('l')))
249 c = '\0';
250 add:
251 if (l >= len) {
252 if (allocated) {
253 size_t nlen = len + 1024;
254 char *nbuf = realloc(buf, nlen);
255 if (nbuf == NULL)
256 goto restore;
257 buf = nbuf;
258 len = nlen;
259 } else {
260 if (flags & GETPASS_BUF_LIMIT) {
261 beep();
262 continue;
264 if (c == '\0' && l > 0)
265 l--;
266 else
267 continue;
271 if (flags & GETPASS_7BIT)
272 c &= 0x7f;
273 if ((flags & GETPASS_FORCE_LOWER) && isupper((unsigned char)c))
274 c = tolower((unsigned char)c);
275 if ((flags & GETPASS_FORCE_UPPER) && islower((unsigned char)c))
276 c = toupper((unsigned char)c);
278 buf[l++] = c;
279 if (c) {
280 if (flags & GETPASS_ECHO_STAR)
281 (void)write(fd[1], "*", 1);
282 else if (flags & GETPASS_ECHO)
283 (void)write(fd[1], isprint((unsigned char)c) ?
284 &c : "?", 1);
287 good = true;
289 restore:
290 if (havetty) {
291 c = errno;
292 (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &gt);
293 errno = c;
295 out:
296 if (good && (flags & GETPASS_ECHO_NL))
297 (void)write(fd[1], "\n", 1);
299 if (opentty) {
300 c = errno;
301 (void)close(fd[0]);
302 errno = c;
305 if (good)
306 return buf;
308 if (sig) {
309 if ((flags & GETPASS_NO_SIGNAL) == 0)
310 (void)raise(sig);
311 errno = EINTR;
313 memset(buf, 0, len);
314 if (allocated)
315 free(buf);
316 return NULL;
319 char *
320 getpass_r(const char *prompt, char *buf, size_t len)
322 return getpassfd(prompt, buf, len, NULL, GETPASS_ECHO_NL, 0);
325 char *
326 getpass(const char *prompt)
328 static char e[] = "";
329 static char *buf;
330 static long bufsiz;
331 char *rv;
334 * Strictly speaking we could double allocate here, if we get
335 * called at the same time, but this function is not re-entrant
336 * anyway and it is not supposed to work if called concurrently.
338 if (buf == NULL) {
339 if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1)
340 return e;
341 if ((buf = malloc((size_t)bufsiz)) == NULL)
342 return e;
345 if ((rv = getpass_r(prompt, buf, (size_t)bufsiz)) == NULL)
346 return e;
348 return rv;
351 #ifdef TEST
353 main(int argc, char *argv[])
355 char buf[28];
356 printf("[%s]\n", getpassfd("foo>", buf, sizeof(buf), NULL,
357 GETPASS_ECHO_STAR|GETPASS_ECHO_NL, 2));
358 return 0;
360 #endif