]> diplodocus.org Git - nmh/blob - uip/rcvtty.c
lock_file.c: close(2) file descriptor on failure, avoiding leak.
[nmh] / uip / rcvtty.c
1 /* rcvtty.c -- a rcvmail program (a lot like rcvalert) handling IPC ttys
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 /* Changed to use getutent() and friends. Assumes that when getutent() exists,
9 * a number of other things also exist. Please check.
10 * Ruud de Rooij <ruud@ruud.org> Sun, 28 May 2000 17:28:55 +0200
11 */
12
13 #include <h/mh.h>
14 #include <h/signals.h>
15 #include <setjmp.h>
16 #include <h/rcvmail.h>
17 #include <h/scansbr.h>
18 #include <h/tws.h>
19 #include <h/mts.h>
20 #include <h/utils.h>
21 #include "sbr/m_mktemp.h"
22 #include <fcntl.h>
23
24 #ifdef HAVE_GETUTXENT
25 #include <utmpx.h>
26 #endif /* HAVE_GETUTXENT */
27
28 #define SCANFMT \
29 "%2(hour{dtimenow}):%02(min{dtimenow}): %<(size)%5(size) %>%<{encrypted}E%>\
30 %<(mymbox{from})%<{to}To:%14(friendly{to})%>%>%<(zero)%17(friendly{from})%> \
31 %{subject}%<{body}<<%{body}>>%>"
32
33 #define RCVTTY_SWITCHES \
34 X("biff", 0, BIFFSW) \
35 X("form formatfile", 0, FORMSW) \
36 X("format string", 5, FMTSW) \
37 X("width columns", 0, WIDTHSW) \
38 X("newline", 0, NLSW) \
39 X("nonewline", 0, NNLSW) \
40 X("bell", 0, BELSW) \
41 X("nobell", 0, NBELSW) \
42 X("version", 0, VERSIONSW) \
43 X("help", 0, HELPSW) \
44
45 #define X(sw, minchars, id) id,
46 DEFINE_SWITCH_ENUM(RCVTTY);
47 #undef X
48
49 #define X(sw, minchars, id) { sw, minchars, id },
50 DEFINE_SWITCH_ARRAY(RCVTTY, switches);
51 #undef X
52
53 static jmp_buf myctx;
54 static int bell = 1;
55 static int newline = 1;
56 static int biff = 0;
57 static int width = -1;
58 static char *form = NULL;
59 static char *format = NULL;
60
61 /*
62 * static prototypes
63 */
64 static void alrmser (int);
65 static int message_fd (char **);
66 static int header_fd (void);
67 #if HAVE_GETUTXENT
68 static void alert (char *, int);
69 #endif /* HAVE_GETUTXENT */
70
71
72 int
73 main (int argc, char **argv)
74 {
75 int md, vecp = 0;
76 char *cp, *user, buf[BUFSIZ], tty[BUFSIZ];
77 char **argp, **arguments, *vec[MAXARGS];
78 struct utmpx *utp;
79
80 if (nmh_init(argv[0], 2)) { return 1; }
81
82 mts_init ();
83 arguments = getarguments (invo_name, argc, argv, 1);
84 argp = arguments;
85
86 while ((cp = *argp++)) {
87 if (*cp == '-') {
88 switch (smatch (++cp, switches)) {
89 case AMBIGSW:
90 ambigsw (cp, switches);
91 done (1);
92 case UNKWNSW:
93 vec[vecp++] = --cp;
94 continue;
95
96 case HELPSW:
97 snprintf (buf, sizeof(buf), "%s [command ...]", invo_name);
98 print_help (buf, switches, 1);
99 done (0);
100 case VERSIONSW:
101 print_version(invo_name);
102 done (0);
103
104 case BIFFSW:
105 biff = 1;
106 continue;
107
108 case FORMSW:
109 if (!(form = *argp++) || *form == '-')
110 adios (NULL, "missing argument to %s", argp[-2]);
111 format = NULL;
112 continue;
113 case FMTSW:
114 if (!(format = *argp++) || *format == '-')
115 adios (NULL, "missing argument to %s", argp[-2]);
116 form = NULL;
117 continue;
118
119 case WIDTHSW:
120 if (!(cp = *argp++) || *cp == '-')
121 adios(NULL, "missing argument to %s", argp[-2]);
122 width = atoi(cp);
123 continue;
124 case NLSW:
125 newline = 1;
126 continue;
127 case NNLSW:
128 newline = 0;
129 continue;
130 case BELSW:
131 bell = 1;
132 continue;
133 case NBELSW:
134 bell = 0;
135 continue;
136
137 }
138 }
139 vec[vecp++] = cp;
140 }
141 vec[vecp] = 0;
142
143 if ((md = vecp ? message_fd (vec) : header_fd ()) == NOTOK)
144 exit (RCV_MBX);
145
146 user = getusername();
147
148 #if HAVE_GETUTXENT
149 setutxent();
150 while ((utp = getutxent()) != NULL) {
151 if (utp->ut_type == USER_PROCESS && utp->ut_user[0] != 0
152 && utp->ut_line[0] != 0
153 && strncmp (user, utp->ut_user, sizeof(utp->ut_user)) == 0) {
154 strncpy (tty, utp->ut_line, sizeof(utp->ut_line));
155 alert (tty, md);
156 }
157 }
158 endutxent();
159 #else
160 NMH_UNUSED (tty);
161 NMH_UNUSED (utp);
162 #endif /* HAVE_GETUTXENT */
163
164 exit (RCV_MOK);
165 }
166
167
168 static void
169 alrmser (int i)
170 {
171 NMH_UNUSED (i);
172
173 longjmp (myctx, 1);
174 }
175
176
177 static int
178 message_fd (char **vec)
179 {
180 pid_t child_id;
181 int bytes, seconds;
182 int fd;
183 char *tfile;
184 struct stat st;
185
186 if ((tfile = m_mktemp2(NULL, invo_name, &fd, NULL)) == NULL) {
187 inform("unable to create temporary file in %s", get_temp_dir());
188 return NOTOK;
189 }
190 (void) m_unlink(tfile); /* Use fd, no longer need the file name. */
191
192 if ((child_id = fork()) == NOTOK) {
193 /* fork error */
194 close (fd);
195 return header_fd ();
196 }
197 if (child_id) {
198 /* parent process */
199 if (!setjmp (myctx)) {
200 SIGNAL (SIGALRM, alrmser);
201 bytes = fstat(fileno (stdin), &st) != NOTOK ? (int) st.st_size : 100;
202
203 /* amount of time to wait depends on message size */
204 if (bytes <= 100) {
205 /* give at least 5 minutes */
206 seconds = 300;
207 } else if (bytes >= 90000) {
208 /* but 30 minutes should be long enough */
209 seconds = 1800;
210 } else {
211 seconds = (bytes / 60) + 300;
212 }
213 alarm ((unsigned int) seconds);
214 pidwait(child_id, OK);
215 alarm (0);
216
217 if (fstat (fd, &st) != NOTOK && st.st_size > 0)
218 return fd;
219 } else {
220 /*
221 * Ruthlessly kill the child and anything
222 * else in its process group.
223 */
224 killpg(child_id, SIGKILL);
225 }
226 close (fd);
227 return header_fd ();
228 }
229
230 /* child process */
231 rewind (stdin);
232 if (dup2 (fd, 1) == NOTOK || dup2 (fd, 2) == NOTOK)
233 _exit (-1);
234 closefds (3);
235 setpgid(0, getpid()); /* put in own process group */
236 if (execvp (vec[0], vec) == NOTOK) {
237 _exit (-1);
238 }
239
240 return NOTOK;
241 }
242
243
244 static int
245 header_fd (void)
246 {
247 int fd;
248 char *nfs;
249 char *tfile = NULL;
250 charstring_t scanl = NULL;
251
252 if ((tfile = m_mktemp2(NULL, invo_name, &fd, NULL)) == NULL) {
253 inform("unable to create temporary file in %s", get_temp_dir());
254 return NOTOK;
255 }
256 (void) m_unlink(tfile); /* Use fd, no longer need the file name. */
257
258 rewind (stdin);
259
260 /* get new format string */
261 nfs = new_fs (form, format, SCANFMT);
262 scan (stdin, 0, 0, nfs, width, 0, 0, NULL, 0L, 0, &scanl);
263 scan_finished ();
264 if (newline) {
265 if (write (fd, "\n\r", 2) < 0) {
266 advise (tfile, "write LF/CR");
267 }
268 }
269 if (write (fd, charstring_buffer (scanl), charstring_bytes (scanl)) < 0) {
270 advise (tfile, "write");
271 }
272 charstring_free (scanl);
273 if (bell) {
274 if (write (fd, "\007", 1) < 0) {
275 advise (tfile, "write BEL");
276 }
277 }
278
279 return fd;
280 }
281
282
283 #if HAVE_GETUTXENT
284 static void
285 alert (char *tty, int md)
286 {
287 int i, td, mask;
288 char buffer[BUFSIZ], ttyspec[BUFSIZ];
289 struct stat st;
290
291 snprintf (ttyspec, sizeof(ttyspec), "/dev/%s", tty);
292
293 /*
294 * The mask depends on whether we are checking for
295 * write permission based on `biff' or `mesg'.
296 */
297 mask = biff ? S_IEXEC : (S_IWRITE >> 3);
298 if (stat (ttyspec, &st) == NOTOK || (st.st_mode & mask) == 0)
299 return;
300
301 if (setjmp (myctx)) {
302 alarm (0);
303 return;
304 }
305 SIGNAL (SIGALRM, alrmser);
306 alarm (2);
307 td = open (ttyspec, O_WRONLY);
308 alarm (0);
309 if (td == NOTOK)
310 return;
311
312 lseek(md, 0, SEEK_SET);
313
314 while ((i = read (md, buffer, sizeof(buffer))) > 0)
315 if (write (td, buffer, i) != i)
316 break;
317
318 close (td);
319 }
320 #endif /* HAVE_GETUTXENT */