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