69a903116fe56e9ed9633b962a56ada26fa756fa
[mmh] / uip / rcvtty.c
1
2 /*
3  * rcvtty.c -- a rcvmail program (a lot like rcvalert) handling IPC ttys
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include <h/signals.h>
10 #include <h/rcvmail.h>
11 #include <h/scansbr.h>
12 #include <zotnet/tws/tws.h>
13 #include <zotnet/mts/mts.h>
14 #include <signal.h>
15 #include <fcntl.h>
16
17 #include <utmp.h>
18 #ifndef UTMP_FILE
19 # ifdef _PATH_UTMP
20 #  define UTMP_FILE _PATH_UTMP
21 # else
22 #  define UTMP_FILE "/etc/utmp"
23 # endif
24 #endif
25
26 #define SCANFMT \
27 "%2(hour{dtimenow}):%02(min{dtimenow}): %<(size)%5(size) %>%<{encrypted}E%>\
28 %<(mymbox{from})%<{to}To:%14(friendly{to})%>%>%<(zero)%17(friendly{from})%>  \
29 %{subject}%<{body}<<%{body}>>%>"
30
31 static struct swit switches[] = {
32 #define BIFFSW  0
33     { "biff", 0 },
34 #define FORMSW  1
35     { "form formatfile", 0 },
36 #define FMTSW   2
37     { "format string", 5 },
38 #define WIDTHSW 3
39     { "width columns", 0 },
40 #define NLSW    4
41     { "newline", 0 },
42 #define NNLSW   5
43     { "nonewline", 0 },
44 #define BELSW   6
45     { "bell", 0 },
46 #define NBELSW  7
47     { "nobell", 0 },
48 #define VERSIONSW 8
49     { "version", 0 },
50 #define HELPSW  9
51     { "help", 0 },
52     { NULL, 0 }
53 };
54
55 static jmp_buf myctx;
56 static int bell = 1;
57 static int newline = 1;
58 static int biff = 0;
59 static int width = 0;
60 static char *form = NULL;
61 static char *format = NULL;
62
63 /*
64  * external prototypes
65  */
66 char *getusername(void);
67
68 /*
69  * static prototypes
70  */
71 static RETSIGTYPE alrmser (int);
72 static int message_fd (char **);
73 static int header_fd (void);
74 static void alert (char *, int);
75
76
77 int
78 main (int argc, char **argv)
79 {
80     int md, vecp = 0;
81     char *cp, *user, buf[BUFSIZ], tty[BUFSIZ];
82     char **argp, **arguments, *vec[MAXARGS];
83     struct utmp ut;
84     register FILE *uf;
85
86 #ifdef LOCALE
87     setlocale(LC_ALL, "");
88 #endif
89     invo_name = r1bindex (argv[0], '/');
90
91     /* read user profile/context */
92     context_read();
93
94     mts_init (invo_name);
95     arguments = getarguments (invo_name, argc, argv, 1);
96     argp = arguments;
97
98     while ((cp = *argp++)) {
99         if (*cp == '-') {
100             switch (smatch (++cp, switches)) {
101                 case AMBIGSW: 
102                     ambigsw (cp, switches);
103                     done (1);
104                 case UNKWNSW: 
105                     vec[vecp++] = --cp;
106                     continue;
107
108                 case HELPSW: 
109                     snprintf (buf, sizeof(buf), "%s [command ...]", invo_name);
110                     print_help (buf, switches, 1);
111                     done (1);
112                 case VERSIONSW:
113                     print_version(invo_name);
114                     done (1);
115
116                 case BIFFSW:
117                     biff = 1;
118                     continue;
119
120                 case FORMSW: 
121                     if (!(form = *argp++) || *form == '-')
122                         adios (NULL, "missing argument to %s", argp[-2]);
123                     format = NULL;
124                     continue;
125                 case FMTSW: 
126                     if (!(format = *argp++) || *format == '-')
127                         adios (NULL, "missing argument to %s", argp[-2]);
128                     form = NULL;
129                     continue;
130
131                 case WIDTHSW:
132                     if (!(cp = *argp++) || *cp == '-')
133                         adios(NULL, "missing argument to %s", argp[-2]);
134                     width = atoi(cp);
135                     continue;
136                 case NLSW:
137                     newline = 1;
138                     continue;
139                 case NNLSW:
140                     newline = 0;
141                     continue;
142                 case BELSW:
143                     bell = 1;
144                     continue;
145                 case NBELSW:
146                     bell = 0;
147                     continue;
148
149             }
150         }
151         vec[vecp++] = cp;
152     }
153     vec[vecp] = 0;
154
155     if ((md = vecp ? message_fd (vec) : header_fd ()) == NOTOK)
156         exit (RCV_MBX);
157
158     user = getusername();
159     if ((uf = fopen (UTMP_FILE, "r")) == NULL)
160         exit (RCV_MBX);
161
162     while (fread ((char *) &ut, sizeof(ut), 1, uf) == 1)
163         if (ut.ut_name[0] != 0
164                 && strncmp (user, ut.ut_name, sizeof(ut.ut_name)) == 0) {
165             strncpy (tty, ut.ut_line, sizeof(ut.ut_line));
166             alert (tty, md);
167         }
168
169     fclose (uf);
170     exit (RCV_MOK);
171     return 0;  /* dead code to satisfy the compiler */
172 }
173
174
175 static RETSIGTYPE
176 alrmser (int i)
177 {
178 #ifndef RELIABLE_SIGNALS
179     SIGNAL (SIGALRM, alrmser);
180 #endif
181
182     longjmp (myctx, 1);
183 }
184
185
186 static int
187 message_fd (char **vec)
188 {
189     pid_t child_id;
190     int bytes, fd, seconds;
191     char tmpfil[BUFSIZ];
192     struct stat st;
193
194 #ifdef HAVE_MKSTEMP
195     fd = mkstemp (strncpy (tmpfil, "/tmp/rcvttyXXXXX", sizeof(tmpfil)));
196 #else
197     unlink (mktemp (strncpy (tmpfil, "/tmp/rcvttyXXXXX", sizeof(tmpfil))));
198     if ((fd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
199         return header_fd ();
200     unlink (tmpfil);
201 #endif
202
203     if ((child_id = vfork()) == NOTOK) {
204         /* fork error */
205         close (fd);
206         return header_fd ();
207     } else if (child_id) {
208         /* parent process */
209         if (!setjmp (myctx)) {
210             SIGNAL (SIGALRM, alrmser);
211             bytes = fstat(fileno (stdin), &st) != NOTOK ? (int) st.st_size : 100;
212
213             /* amount of time to wait depends on message size */
214             if (bytes <= 100) {
215                 /* give at least 5 minutes */
216                 seconds = 300;
217             } else if (bytes >= 90000) {
218                 /* but 30 minutes should be long enough */
219                 seconds = 1800;
220             } else {
221                 seconds = (bytes / 60) + 300;
222             }
223             alarm ((unsigned int) seconds);
224             pidwait(child_id, OK);
225             alarm (0);
226
227             if (fstat (fd, &st) != NOTOK && st.st_size > (off_t) 0)
228                 return fd;
229         } else {
230             /*
231              * Ruthlessly kill the child and anything
232              * else in its process group.
233              */
234             KILLPG(child_id, SIGKILL);
235         }
236         close (fd);
237         return header_fd ();
238     }
239
240     /* child process */
241     rewind (stdin);
242     if (dup2 (fd, 1) == NOTOK || dup2 (fd, 2) == NOTOK)
243         _exit (-1);
244     closefds (3);
245     setpgid ((pid_t) 0, getpid ());     /* put in own process group */
246     execvp (vec[0], vec);
247     _exit (-1);
248     return 1;  /* dead code to satisfy compiler */
249 }
250
251
252 static int
253 header_fd (void)
254 {
255     int fd;
256     char *nfs, tmpfil[BUFSIZ];
257
258     strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
259     if ((fd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
260         return NOTOK;
261     unlink (tmpfil);
262
263     rewind (stdin);
264
265     /* get new format string */
266     nfs = new_fs (form, format, SCANFMT);
267     scan (stdin, 0, 0, nfs, width, 0, 0, NULL, 0L, 0);
268     if (newline)
269         write (fd, "\n\r", 2);
270     write (fd, scanl, strlen (scanl));
271     if (bell)
272         write (fd, "\007", 1);
273
274     return fd;
275 }
276
277
278 static void
279 alert (char *tty, int md)
280 {
281     int i, td, mask;
282     char buffer[BUFSIZ], ttyspec[BUFSIZ];
283     struct stat st;
284
285     snprintf (ttyspec, sizeof(ttyspec), "/dev/%s", tty);
286
287     /*
288      * The mask depends on whether we are checking for
289      * write permission based on `biff' or `mesg'.
290      */
291     mask = biff ? S_IEXEC : (S_IWRITE >> 3);
292     if (stat (ttyspec, &st) == NOTOK || (st.st_mode & mask) == 0)
293         return;
294
295     if (!setjmp (myctx)) {
296         SIGNAL (SIGALRM, alrmser);
297         alarm (2);
298         td = open (ttyspec, O_WRONLY);
299         alarm (0);
300         if (td == NOTOK)
301             return;
302     } else {
303         alarm (0);
304         return;
305     }
306
307     lseek (md, (off_t) 0, SEEK_SET);
308
309     while ((i = read (md, buffer, sizeof(buffer))) > 0)
310         if (write (td, buffer, i) != i)
311             break;
312
313     close (td);
314 }
315