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