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