3 * $Id: file.c,v 1.6 2003/11/29 20:36:07 mdw Exp $
5 * File source and target
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the `fw' port forwarder.
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.6 2003/11/29 20:36:07 mdw
33 * Privileged outgoing connections.
35 * Revision 1.5 2002/02/22 23:43:32 mdw
36 * Call @xfree@ rather than @free@.
38 * Revision 1.4 2001/02/03 20:30:03 mdw
39 * Support re-reading config files on SIGHUP.
41 * Revision 1.3 1999/12/22 15:43:47 mdw
44 * Revision 1.2 1999/10/22 22:47:13 mdw
45 * New (empty) endpoint method required.
47 * Revision 1.1 1999/07/26 23:33:32 mdw
48 * New sources and targets.
52 /*----- Header files ------------------------------------------------------*/
62 #include <sys/types.h>
67 #include <mLib/alloc.h>
68 #include <mLib/dstr.h>
69 #include <mLib/fdflags.h>
82 /*----- Data structures ---------------------------------------------------*/
84 /* --- File specification --- */
86 typedef struct fspec {
94 #define FTYPE_GUESS 0u
99 /* --- File options --- */
101 typedef struct fopts {
105 /* --- File data block --- */
107 typedef struct fdata {
113 /* --- File source block --- */
115 typedef struct fsource {
120 /* --- File target block --- */
122 typedef struct ftarget {
127 /*----- Static variables --------------------------------------------------*/
129 static fopts file_opts = { O_TRUNC };
131 static reffd *rstdin, *rstdout;
134 /*----- File endpoint operations ------------------------------------------*/
136 /* --- @wclose@ --- */
138 static void fept_wclose(endpt *e)
146 /* --- @close@ --- */
148 static void fept_close(endpt *e)
158 /* --- Endpoint operations table --- */
160 static endpt_ops fept_ops = { 0, 0, fept_wclose, fept_close };
162 /*----- General operations on sources and targets -------------------------*/
164 /* --- @file_null@ --- *
166 * Arguments: @void *p@ = an uninteresting argument
170 * Use: Removes the reference to @rnull@ when the file closes.
173 static void file_null(void *p)
178 /* --- @file_nullref@ --- *
182 * Returns: A reference to a file descriptor open on /dev/null.
184 * Use: Obtains a file descriptor for /dev/null. The reference
185 * @rnull@ is `weak', so that I only have a descriptor open when
186 * I actually need one.
189 static reffd *file_nullref(void)
195 if ((n = open("/dev/null", O_RDWR)) < 0) {
196 fw_log(-1, "couldn't open `/dev/null': %s", strerror(errno));
199 fdflags(n, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
200 rnull = reffd_init(n);
201 reffd_handler(rnull, file_null, 0);
206 /* --- @file_fspec@ --- *
208 * Arguments: @fspec *f@ = pointer to filespec to fill in
209 * @scanner *sc@ = pointer to scanner to read from
213 * Use: Reads in a file spec.
216 static void file_fspec(fspec *f, scanner *sc)
219 unsigned type = FTYPE_GUESS;
222 /* --- Set up references to @stdin@ and @stdout@ --- */
225 fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
226 fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
227 rstdin = reffd_init(STDIN_FILENO);
228 rstdout = reffd_init(STDOUT_FILENO);
231 /* --- Try to get an access type --- */
238 if (sc->t == CTOK_WORD) {
239 int i = conf_enum(sc, "fd,name,null", c ? ENUM_ABBREV : ENUM_NONE,
242 if (type && sc->t == ':')
246 /* --- Check for a valid file descriptor name --- */
248 if (sc->t == CTOK_WORD && type != FTYPE_NAME && type != FTYPE_NULL) {
249 if (strcmp(sc->d.buf, "stdin") == 0) {
252 } else if (strcmp(sc->d.buf, "stdout") == 0) {
258 else if (isdigit((unsigned char)*sc->d.buf)) {
259 int nfd = atoi(sc->d.buf);
261 case STDIN_FILENO: fd = rstdin; REFFD_INC(rstdin); break;
262 case STDOUT_FILENO: fd = rstdout; REFFD_INC(rstdout); break;
264 fd = reffd_init(nfd);
265 fdflags(nfd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
271 /* --- Sort out what to do as a result --- */
273 if (type == FTYPE_NULL)
274 f->type = FTYPE_NULL;
279 } else if (type == FTYPE_FD) {
280 if (sc->t == CTOK_WORD)
281 error(sc, "bad file descriptor `%s'", sc->d.buf);
283 error(sc, "parse error, expected file descriptor");
286 conf_name(sc, '/', &d);
287 f->type = FTYPE_NAME;
288 f->u.name = xstrdup(d.buf);
295 static void file_read(fdata *f, scanner *sc)
297 file_fspec(&f->in, sc);
300 if (f->out.type == FTYPE_FD && f->out.u.fd == rstdin)
301 f->out.u.fd = rstdout;
304 file_fspec(&f->out, sc);
306 f->fa = fattr_global;
310 /* --- @print@ --- */
312 static void file_pfspec(fspec *f, dstr *d)
316 switch (f->u.fd->fd) {
318 dstr_puts(d, "stdin");
321 dstr_puts(d, "stdout");
324 dstr_putf(d, "fd:%i", f->u.fd->fd);
329 dstr_putf(d, "name:%s", f->u.name);
332 dstr_puts(d, "null");
337 static void file_desc(fdata *f, dstr *d)
339 dstr_puts(d, "file ");
340 file_pfspec(&f->in, d);
342 file_pfspec(&f->out, d);
345 /* --- @option@ --- */
347 static int file_option(fdata *f, scanner *sc)
349 fopts *fo = f ? &f->fo : &file_opts;
351 CONF_BEGIN(sc, "file", "file")
353 /* --- Handle file-specific options --- */
355 if (strcmp(sc->d.buf, "create") == 0) {
359 switch (conf_enum(sc, "no,yes", ENUM_ABBREV, "create status")) {
360 case 0: fo->of &= ~O_CREAT; break;
361 case 1: fo->of |= O_CREAT; break;
366 else if (strcmp(sc->d.buf, "open") == 0) {
370 fo->of &= ~(O_TRUNC | O_APPEND | O_EXCL);
371 switch (conf_enum(sc, "no,truncate,append",
372 ENUM_ABBREV, "open status")) {
373 case 0: fo->of |= O_EXCL; break;
374 case 1: fo->of |= O_TRUNC; break;
375 case 2: fo->of |= O_APPEND; break;
380 /* --- See if it's a file attribute --- */
383 fattr *fa = f ? &f->fa : &fattr_global;
384 if (fattr_option(sc, fa))
388 /* --- Nobody understood it --- */
393 /* --- @endpt@ --- */
395 static endpt *file_endpt(fdata *f, const char *desc)
397 reffd *in = 0, *out = 0;
400 /* --- Make the input file first --- */
402 switch (f->in.type) {
412 if ((fd = open(f->in.u.name, O_RDONLY | O_NONBLOCK)) < 0) {
413 fw_log(-1, "[%s] couldn't open `%s' for reading: %s",
414 desc, f->in.u.name, strerror(errno));
421 /* --- Make the output file second --- */
423 switch (f->out.type) {
425 out = file_nullref();
432 int m = f->fo.of | O_WRONLY | O_NONBLOCK;
435 /* --- First try creating the file --- *
437 * This lets me know whether to set the file attributes or not. It
438 * also stands a chance of noticing people playing silly beggars with
443 (fd = open(f->out.u.name, m | O_EXCL, f->fa.mode)) < 0 &&
444 (errno != EEXIST || (m & O_EXCL))) {
445 fw_log(-1, "[%s] couldn't create `%s': %s",
446 desc, f->out.u.name, strerror(errno));
452 if (fattr_apply(f->out.u.name, &f->fa)) {
453 fw_log(-1, "[%s] couldn't apply file attributes to `%s': %s",
454 desc, f->out.u.name, strerror(errno));
456 } else if ((fd = open(f->out.u.name, m & ~O_CREAT)) < 0) {
457 fw_log(-1, "[%s] couldn't open `%s': %s",
458 desc, f->out.u.name, strerror(errno));
463 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
464 out = reffd_init(fd);
468 /* --- Set up the endpoint --- */
481 /* --- @destroy@ --- */
483 static void file_destroy(fdata *f)
485 if (f->in.type == FTYPE_NAME)
487 if (f->out.type == FTYPE_NAME)
488 xfree(f->out.u.name);
491 /*----- File source description -------------------------------------------*/
493 /* --- @option@ --- */
495 static int fsource_option(source *s, scanner *sc)
497 fsource *fs = (fsource *)s;
498 return (file_option(fs ? &fs->f : 0, sc));
503 static source *fsource_read(scanner *sc)
507 if (!conf_prefix(sc, "file"))
509 fs = CREATE(fsource);
510 fs->s.ops = &fsource_ops;
512 file_read(&fs->f, sc);
516 /* --- @attach@ --- */
518 static void fsource_destroy(source */*s*/);
520 static void fsource_attach(source *s, scanner *sc, target *t)
522 fsource *fs = (fsource *)s;
525 /* --- Set up the source description string --- */
529 file_desc(&fs->f, &d);
530 dstr_puts(&d, " -> ");
531 dstr_puts(&d, t->desc);
532 fs->s.desc = xstrdup(d.buf);
536 /* --- And then create the endpoints --- */
538 if ((ee = t->ops->create(t, fs->s.desc)) == 0)
540 if ((e = file_endpt(&fs->f, fs->s.desc)) == 0) {
546 /* --- Dispose of the source and target now --- */
550 fsource_destroy(&fs->s);
553 /* --- @destroy@ --- */
555 static void fsource_destroy(source *s)
557 fsource *fs = (fsource *)s;
559 file_destroy(&fs->f);
563 /* --- File source operations --- */
565 source_ops fsource_ops = {
567 fsource_option, fsource_read, fsource_attach, fsource_destroy
570 /*----- File target description -------------------------------------------*/
572 /* --- @option@ --- */
574 static int ftarget_option(target *t, scanner *sc)
576 ftarget *ft = (ftarget *)t;
577 return (file_option(ft ? &ft->f : 0, sc));
582 static target *ftarget_read(scanner *sc)
587 if (!conf_prefix(sc, "file"))
589 ft = CREATE(ftarget);
590 ft->t.ops = &ftarget_ops;
591 file_read(&ft->f, sc);
592 file_desc(&ft->f, &d);
593 ft->t.desc = xstrdup(d.buf);
598 /* --- @create@ --- */
600 static endpt *ftarget_create(target *t, const char *desc)
602 ftarget *ft = (ftarget *)t;
603 endpt *e = file_endpt(&ft->f, desc);
607 /* --- @destroy@ --- */
609 static void ftarget_destroy(target *t)
611 ftarget *ft = (ftarget *)t;
612 file_destroy(&ft->f);
617 /* --- File target operations --- */
619 target_ops ftarget_ops = {
621 ftarget_option, ftarget_read, 0, ftarget_create, ftarget_destroy
624 /*----- That's all, folks -------------------------------------------------*/