chiark / gitweb /
Update manual style.
[fwd] / file.c
1 /* -*-c-*-
2  *
3  * $Id: file.c,v 1.4 2001/02/03 20:30:03 mdw Exp $
4  *
5  * File source and target
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the `fw' port forwarder.
13  *
14  * `fw' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * `fw' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with `fw'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: file.c,v $
32  * Revision 1.4  2001/02/03 20:30:03  mdw
33  * Support re-reading config files on SIGHUP.
34  *
35  * Revision 1.3  1999/12/22 15:43:47  mdw
36  * Fix log messages.
37  *
38  * Revision 1.2  1999/10/22 22:47:13  mdw
39  * New (empty) endpoint method required.
40  *
41  * Revision 1.1  1999/07/26 23:33:32  mdw
42  * New sources and targets.
43  *
44  */
45
46 /*----- Header files ------------------------------------------------------*/
47
48 #include "config.h"
49
50 #include <ctype.h>
51 #include <errno.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <unistd.h>
59 #include <fcntl.h>
60
61 #include <mLib/alloc.h>
62 #include <mLib/dstr.h>
63 #include <mLib/fdflags.h>
64 #include <mLib/sub.h>
65
66 #include "conf.h"
67 #include "endpt.h"
68 #include "fattr.h"
69 #include "file.h"
70 #include "fw.h"
71 #include "reffd.h"
72 #include "scan.h"
73 #include "source.h"
74 #include "target.h"
75
76 /*----- Data structures ---------------------------------------------------*/
77
78 /* --- File specification --- */
79
80 typedef struct fspec {
81   unsigned type;
82   union {
83     reffd *fd;
84     char *name;
85   } u;
86 } fspec;
87
88 #define FTYPE_GUESS 0u
89 #define FTYPE_FD 1u
90 #define FTYPE_NAME 2u
91 #define FTYPE_NULL 3u
92
93 /* --- File options --- */
94
95 typedef struct fopts {
96   unsigned of;
97 } fopts;
98
99 /* --- File data block --- */
100
101 typedef struct fdata {
102   fspec in, out;
103   fattr fa;
104   fopts fo;
105 } fdata;
106
107 /* --- File source block --- */
108
109 typedef struct fsource {
110   source s;
111   fdata f;
112 } fsource;  
113
114 /* --- File target block --- */
115
116 typedef struct ftarget {
117   target t;
118   fdata f;
119 } ftarget;
120
121 /*----- Static variables --------------------------------------------------*/
122
123 static fopts file_opts = { O_TRUNC };
124
125 static reffd *rstdin, *rstdout;
126 static reffd *rnull;
127
128 /*----- File endpoint operations ------------------------------------------*/
129
130 /* --- @wclose@ --- */
131
132 static void fept_wclose(endpt *e)
133 {
134   if (e->out) {
135     REFFD_DEC(e->out);
136     e->out = 0;
137   }
138 }
139
140 /* --- @close@ --- */
141
142 static void fept_close(endpt *e)
143 {
144   if (e->in)
145     REFFD_DEC(e->in);
146   if (e->out)
147     REFFD_DEC(e->out);
148   fw_dec();
149   DESTROY(e);
150 }
151
152 /* --- Endpoint operations table --- */
153
154 static endpt_ops fept_ops = { 0, 0, fept_wclose, fept_close };
155
156 /*----- General operations on sources and targets -------------------------*/
157
158 /* --- @file_null@ --- *
159  *
160  * Arguments:   @void *p@ = an uninteresting argument
161  *
162  * Returns:     ---
163  *
164  * Use:         Removes the reference to @rnull@ when the file closes.
165  */
166
167 static void file_null(void *p)
168 {
169   rnull = 0;
170 }
171
172 /* --- @file_nullref@ --- *
173  *
174  * Arguments:   ---
175  *
176  * Returns:     A reference to a file descriptor open on /dev/null.
177  *
178  * Use:         Obtains a file descriptor for /dev/null.  The reference
179  *              @rnull@ is `weak', so that I only have a descriptor open when
180  *              I actually need one.
181  */
182
183 static reffd *file_nullref(void)
184 {
185   if (rnull)
186     REFFD_INC(rnull);
187   else {
188     int n;
189     if ((n = open("/dev/null", O_RDWR)) < 0) {
190       fw_log(-1, "couldn't open `/dev/null': %s", strerror(errno));
191       return (0);
192     }
193     fdflags(n, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
194     rnull = reffd_init(n);
195     reffd_handler(rnull, file_null, 0);
196   }
197   return (rnull);
198 }  
199
200 /* --- @file_fspec@ --- *
201  *
202  * Arguments:   @fspec *f@ = pointer to filespec to fill in
203  *              @scanner *sc@ = pointer to scanner to read from
204  *
205  * Returns:     ---
206  *
207  * Use:         Reads in a file spec.
208  */
209
210 static void file_fspec(fspec *f, scanner *sc)
211 {
212   int c = 0;
213   unsigned type = FTYPE_GUESS;
214   reffd *fd = 0;
215
216   /* --- Set up references to @stdin@ and @stdout@ --- */
217
218   if (!rstdin) {
219     fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
220     fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
221     rstdin = reffd_init(STDIN_FILENO);
222     rstdout = reffd_init(STDOUT_FILENO);
223   }
224
225   /* --- Try to get an access type --- */
226
227   if (sc->t == ':') {
228     c = 1;
229     token(sc);
230   }
231
232   if (sc->t == CTOK_WORD) {
233     int i = conf_enum(sc, "fd,name,null", c ? ENUM_ABBREV : ENUM_NONE,
234                       "file access type");
235     type = i + 1;
236     if (type && sc->t == ':')
237       token(sc);
238   }
239
240   /* --- Check for a valid file descriptor name --- */
241
242   if (sc->t == CTOK_WORD && type != FTYPE_NAME && type != FTYPE_NULL) {
243     if (strcmp(sc->d.buf, "stdin") == 0) {
244       fd = rstdin;
245       REFFD_INC(rstdin);
246     } else if (strcmp(sc->d.buf, "stdout") == 0) {
247       fd = rstdout;
248       REFFD_INC(rstdout);
249     }
250     if (fd)
251       REFFD_INC(fd);
252     else if (isdigit((unsigned char)*sc->d.buf)) {
253       int nfd = atoi(sc->d.buf);
254       switch (nfd) {
255         case STDIN_FILENO: fd = rstdin; REFFD_INC(rstdin); break;
256         case STDOUT_FILENO: fd = rstdout; REFFD_INC(rstdout); break;
257         default:
258           fd = reffd_init(nfd);
259           fdflags(nfd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
260           break;
261       }
262     }
263   }
264
265   /* --- Sort out what to do as a result --- */
266
267   if (type == FTYPE_NULL)
268     f->type = FTYPE_NULL;
269   else if (fd) {
270     f->type = FTYPE_FD;
271     f->u.fd = fd;
272     token(sc);  
273   } else if (type == FTYPE_FD) {
274     if (sc->t == CTOK_WORD)
275       error(sc, "bad file descriptor `%s'", sc->d.buf);
276     else
277       error(sc, "parse error, expected file descriptor");
278   } else {
279     dstr d = DSTR_INIT;
280     conf_name(sc, '/', &d);
281     f->type = FTYPE_NAME;
282     f->u.name = xstrdup(d.buf);
283     dstr_destroy(&d);
284   }
285 }
286
287 /* --- @read@ --- */
288
289 static void file_read(fdata *f, scanner *sc)
290 {
291   file_fspec(&f->in, sc);
292   if (sc->t != ',') {
293     f->out = f->in;
294     if (f->out.type == FTYPE_FD && f->out.u.fd == rstdin)
295       f->out.u.fd = rstdout;
296   } else {
297     token(sc);
298     file_fspec(&f->out, sc);
299   }
300   f->fa = fattr_global;
301   f->fo = file_opts;
302 }
303
304 /* --- @print@ --- */
305
306 static void file_pfspec(fspec *f, dstr *d)
307 {
308   switch (f->type) {
309     case FTYPE_FD:
310       switch (f->u.fd->fd) {
311         case STDIN_FILENO:
312           dstr_puts(d, "stdin");
313           break;
314         case STDOUT_FILENO:
315           dstr_puts(d, "stdout");
316           break;
317         default:
318           dstr_putf(d, "fd:%i", f->u.fd->fd);
319           break;
320       }
321       break;
322     case FTYPE_NAME:
323       dstr_putf(d, "name:%s", f->u.name);
324       break;
325     case FTYPE_NULL:
326       dstr_puts(d, "null");
327       break;
328   }       
329 }
330
331 static void file_desc(fdata *f, dstr *d)
332 {
333   dstr_puts(d, "file ");
334   file_pfspec(&f->in, d);
335   dstr_puts(d, ", ");
336   file_pfspec(&f->out, d);
337 }
338
339 /* --- @option@ --- */
340
341 static int file_option(fdata *f, scanner *sc)
342 {
343   fopts *fo = f ? &f->fo : &file_opts;
344
345   CONF_BEGIN(sc, "file", "file")
346
347   /* --- Handle file-specific options --- */
348
349   if (strcmp(sc->d.buf, "create") == 0) {
350     token(sc);
351     if (sc->t == '=')
352       token(sc);
353     switch (conf_enum(sc, "no,yes", ENUM_ABBREV, "create status")) {
354       case 0: fo->of &= ~O_CREAT; break;
355       case 1: fo->of |= O_CREAT; break;
356     }
357     CONF_ACCEPT;
358   }
359
360   else if (strcmp(sc->d.buf, "open") == 0) {
361     token(sc);
362     if (sc->t == '=')
363       token(sc);
364     fo->of &= ~(O_TRUNC | O_APPEND | O_EXCL);
365     switch (conf_enum(sc, "no,truncate,append",
366                       ENUM_ABBREV, "open status")) {
367       case 0: fo->of |= O_EXCL; break;
368       case 1: fo->of |= O_TRUNC; break;
369       case 2: fo->of |= O_APPEND; break;
370     }
371     CONF_ACCEPT;
372   }
373
374   /* --- See if it's a file attribute --- */
375
376   {
377     fattr *fa = f ? &f->fa : &fattr_global;
378     if (fattr_option(sc, fa))
379       CONF_ACCEPT;
380   }
381
382   /* --- Nobody understood it --- */
383
384   CONF_END;
385 }
386
387 /* --- @endpt@ --- */
388
389 static endpt *file_endpt(fdata *f, const char *desc)
390 {
391   reffd *in = 0, *out = 0;
392   endpt *e;
393
394   /* --- Make the input file first --- */
395
396   switch (f->in.type) {
397     case FTYPE_NULL:
398       in = file_nullref();
399       break;
400     case FTYPE_FD:
401       in = f->in.u.fd;
402       REFFD_INC(in);
403       break;
404     case FTYPE_NAME: {
405       int fd;
406       if ((fd = open(f->in.u.name, O_RDONLY | O_NONBLOCK)) < 0) {
407         fw_log(-1, "[%s] couldn't open `%s' for reading: %s",
408                desc, f->in.u.name, strerror(errno));
409         return (0);
410       }
411       in = reffd_init(fd);
412     } break;
413   }
414
415   /* --- Make the output file second --- */
416
417   switch (f->out.type) {
418     case FTYPE_NULL:
419       out = file_nullref();
420       break;
421     case FTYPE_FD:
422       out = f->out.u.fd;
423       REFFD_INC(out);
424       break;
425     case FTYPE_NAME: {
426       int m = f->fo.of | O_WRONLY | O_NONBLOCK;
427       int fd = -1;
428
429       /* --- First try creating the file --- *
430        *
431        * This lets me know whether to set the file attributes or not.  It
432        * also stands a chance of noticing people playing silly beggars with
433        * dangling symlinks.
434        */
435
436       if ((m & O_CREAT) &&
437           (fd = open(f->out.u.name, m | O_EXCL, f->fa.mode)) < 0 &&
438           (errno != EEXIST || (m & O_EXCL))) {
439         fw_log(-1, "[%s] couldn't create `%s': %s",
440                desc, f->out.u.name, strerror(errno));
441         REFFD_DEC(in);
442         return (0);
443       }
444
445       if (fd != -1) {
446         if (fattr_apply(f->out.u.name, &f->fa)) {
447           fw_log(-1, "[%s] couldn't apply file attributes to `%s': %s",
448                  desc, f->out.u.name, strerror(errno));
449         }
450       } else if ((fd = open(f->out.u.name, m & ~O_CREAT)) < 0) {
451         fw_log(-1, "[%s] couldn't open `%s': %s",
452                desc, f->out.u.name, strerror(errno));
453         REFFD_DEC(in);
454         return (0);
455       }
456
457       fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
458       out = reffd_init(fd);
459     } break;
460   }
461
462   /* --- Set up the endpoint --- */
463
464   e = CREATE(endpt);
465   e->ops = &fept_ops;
466   e->other = 0;
467   e->f = EPF_FILE;
468   e->t = 0;
469   e->in = in;
470   e->out = out;
471   fw_inc();
472   return (e);
473 }
474
475 /* --- @destroy@ --- */
476
477 static void file_destroy(fdata *f)
478 {
479   if (f->in.type == FTYPE_NAME)
480     free(f->in.u.name);
481   if (f->out.type == FTYPE_NAME)
482     free(f->out.u.name);
483 }
484
485 /*----- File source description -------------------------------------------*/
486
487 /* --- @option@ --- */
488
489 static int fsource_option(source *s, scanner *sc)
490 {
491   fsource *fs = (fsource *)s;
492   return (file_option(fs ? &fs->f : 0, sc));
493 }
494
495 /* --- @read@ --- */
496
497 static source *fsource_read(scanner *sc)
498 {
499   fsource *fs;
500
501   if (!conf_prefix(sc, "file"))
502     return (0);
503   fs = CREATE(fsource);
504   fs->s.ops = &fsource_ops;
505   fs->s.desc = 0;
506   file_read(&fs->f, sc);
507   return (&fs->s);
508 }
509
510 /* --- @attach@ --- */
511
512 static void fsource_destroy(source */*s*/);
513
514 static void fsource_attach(source *s, scanner *sc, target *t)
515 {
516   fsource *fs = (fsource *)s;
517   endpt *e, *ee;
518
519   /* --- Set up the source description string --- */
520
521   {
522     dstr d = DSTR_INIT;
523     file_desc(&fs->f, &d);
524     dstr_puts(&d, " -> ");
525     dstr_puts(&d, t->desc);
526     fs->s.desc = xstrdup(d.buf);
527     dstr_destroy(&d);
528   }
529
530   /* --- And then create the endpoints --- */
531
532   if ((ee = t->ops->create(t, fs->s.desc)) == 0)
533     goto tidy;
534   if ((e = file_endpt(&fs->f, fs->s.desc)) == 0) {
535     ee->ops->close(ee);
536     goto tidy;
537   }
538   endpt_join(e, ee);
539
540   /* --- Dispose of the source and target now --- */
541
542 tidy:
543   t->ops->destroy(t);
544   fsource_destroy(&fs->s);
545 }
546
547 /* --- @destroy@ --- */
548
549 static void fsource_destroy(source *s)
550 {
551   fsource *fs = (fsource *)s;
552   free(fs->s.desc);
553   file_destroy(&fs->f);
554   DESTROY(fs);
555 }
556
557 /* --- File source operations --- */
558
559 source_ops fsource_ops = {
560   "file",
561   fsource_option, fsource_read, fsource_attach, fsource_destroy
562 };
563
564 /*----- File target description -------------------------------------------*/
565
566 /* --- @option@ --- */
567
568 static int ftarget_option(target *t, scanner *sc)
569 {
570   ftarget *ft = (ftarget *)t;
571   return (file_option(ft ? &ft->f : 0, sc));
572 }
573
574 /* --- @read@ --- */
575
576 static target *ftarget_read(scanner *sc)
577 {
578   ftarget *ft;
579   dstr d = DSTR_INIT;
580
581   if (!conf_prefix(sc, "file"))
582     return (0);
583   ft = CREATE(ftarget);
584   ft->t.ops = &ftarget_ops;
585   file_read(&ft->f, sc);
586   file_desc(&ft->f, &d);
587   ft->t.desc = xstrdup(d.buf);
588   dstr_destroy(&d);
589   return (&ft->t);
590 }
591
592 /* --- @create@ --- */
593
594 static endpt *ftarget_create(target *t, const char *desc)
595 {
596   ftarget *ft = (ftarget *)t;
597   endpt *e = file_endpt(&ft->f, desc);
598   return (e);
599 }
600
601 /* --- @destroy@ --- */
602
603 static void ftarget_destroy(target *t)
604 {
605   ftarget *ft = (ftarget *)t;
606   file_destroy(&ft->f);
607   free(ft->t.desc);
608   DESTROY(ft);
609 }
610
611 /* --- File target operations --- */
612
613 target_ops ftarget_ops = {
614   "file",
615   ftarget_option, ftarget_read, ftarget_create, ftarget_destroy
616 };
617
618 /*----- That's all, folks -------------------------------------------------*/