3 * File source and target
5 * (c) 1999 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the `fwd' port forwarder.
12 * `fwd' is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * `fwd' is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with `fwd'; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Data structures ---------------------------------------------------*/
31 /* --- File specification --- */
33 typedef struct fspec {
41 #define FTYPE_GUESS 0u
46 /* --- File options --- */
48 typedef struct fopts {
52 /* --- File data block --- */
54 typedef struct fdata {
60 /* --- File source block --- */
62 typedef struct fsource {
67 /* --- File target block --- */
69 typedef struct ftarget {
74 /*----- Static variables --------------------------------------------------*/
76 static fopts file_opts = { O_TRUNC };
78 static reffd *rstdin, *rstdout;
81 /*----- File endpoint operations ------------------------------------------*/
83 /* --- @wclose@ --- */
85 static void fept_wclose(endpt *e)
95 static void fept_close(endpt *e)
105 /* --- Endpoint operations table --- */
107 static endpt_ops fept_ops = { 0, 0, fept_wclose, fept_close };
109 /*----- General operations on sources and targets -------------------------*/
111 /* --- @file_null@ --- *
113 * Arguments: @void *p@ = an uninteresting argument
117 * Use: Removes the reference to @rnull@ when the file closes.
120 static void file_null(void *p)
125 /* --- @file_nullref@ --- *
129 * Returns: A reference to a file descriptor open on /dev/null.
131 * Use: Obtains a file descriptor for /dev/null. The reference
132 * @rnull@ is `weak', so that I only have a descriptor open when
133 * I actually need one.
136 static reffd *file_nullref(void)
142 if ((n = open("/dev/null", O_RDWR)) < 0) {
143 fw_log(-1, "couldn't open `/dev/null': %s", strerror(errno));
146 fdflags(n, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
147 rnull = reffd_init(n);
148 reffd_handler(rnull, file_null, 0);
153 /* --- @file_fspec@ --- *
155 * Arguments: @fspec *f@ = pointer to filespec to fill in
156 * @scanner *sc@ = pointer to scanner to read from
160 * Use: Reads in a file spec.
163 static void file_fspec(fspec *f, scanner *sc)
166 unsigned type = FTYPE_GUESS;
169 /* --- Set up references to @stdin@ and @stdout@ --- */
172 fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
173 fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
174 rstdin = reffd_init(STDIN_FILENO);
175 rstdout = reffd_init(STDOUT_FILENO);
178 /* --- Try to get an access type --- */
185 if (sc->t == CTOK_WORD) {
186 int i = conf_enum(sc, "fd,name,null", c ? ENUM_ABBREV : ENUM_NONE,
189 if (type && sc->t == ':')
193 /* --- Check for a valid file descriptor name --- */
195 if (sc->t == CTOK_WORD && type != FTYPE_NAME && type != FTYPE_NULL) {
196 if (strcmp(sc->d.buf, "stdin") == 0) {
199 } else if (strcmp(sc->d.buf, "stdout") == 0) {
205 else if (isdigit((unsigned char)*sc->d.buf)) {
206 int nfd = atoi(sc->d.buf);
208 case STDIN_FILENO: fd = rstdin; REFFD_INC(rstdin); break;
209 case STDOUT_FILENO: fd = rstdout; REFFD_INC(rstdout); break;
211 fd = reffd_init(nfd);
212 fdflags(nfd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
218 /* --- Sort out what to do as a result --- */
220 if (type == FTYPE_NULL)
221 f->type = FTYPE_NULL;
226 } else if (type == FTYPE_FD) {
227 if (sc->t == CTOK_WORD)
228 error(sc, "bad file descriptor `%s'", sc->d.buf);
230 error(sc, "parse error, expected file descriptor");
234 f->type = FTYPE_NAME;
235 f->u.name = xstrdup(d.buf);
242 static void file_read(fdata *f, scanner *sc)
244 file_fspec(&f->in, sc);
247 file_fspec(&f->out, sc);
250 if (f->out.type == FTYPE_FD && f->out.u.fd == rstdin)
251 f->out.u.fd = rstdout;
252 else if (f->out.type == FTYPE_NAME)
253 f->out.u.name = xstrdup(f->in.u.name);
255 f->fa = fattr_global;
259 /* --- @print@ --- */
261 static void file_pfspec(fspec *f, dstr *d)
265 switch (f->u.fd->fd) {
267 dstr_puts(d, "stdin");
270 dstr_puts(d, "stdout");
273 dstr_putf(d, "fd:%i", f->u.fd->fd);
278 dstr_putf(d, "name:%s", f->u.name);
281 dstr_puts(d, "null");
286 static void file_desc(fdata *f, dstr *d)
288 dstr_puts(d, "file ");
289 file_pfspec(&f->in, d);
291 file_pfspec(&f->out, d);
294 /* --- @option@ --- */
296 static int file_option(fdata *f, scanner *sc)
298 fopts *fo = f ? &f->fo : &file_opts;
300 CONF_BEGIN(sc, "file", "file")
302 /* --- Handle file-specific options --- */
304 if (strcmp(sc->d.buf, "create") == 0) {
308 switch (conf_enum(sc, "no,yes", ENUM_ABBREV, "create status")) {
309 case 0: fo->of &= ~O_CREAT; break;
310 case 1: fo->of |= O_CREAT; break;
315 else if (strcmp(sc->d.buf, "open") == 0) {
319 fo->of &= ~(O_TRUNC | O_APPEND | O_EXCL);
320 switch (conf_enum(sc, "no,truncate,append",
321 ENUM_ABBREV, "open status")) {
322 case 0: fo->of |= O_EXCL; break;
323 case 1: fo->of |= O_TRUNC; break;
324 case 2: fo->of |= O_APPEND; break;
329 /* --- See if it's a file attribute --- */
332 fattr *fa = f ? &f->fa : &fattr_global;
333 if (fattr_option(sc, fa))
337 /* --- Nobody understood it --- */
342 /* --- @endpt@ --- */
344 static endpt *file_endpt(fdata *f, const char *desc)
346 reffd *in = 0, *out = 0;
349 /* --- Make the input file first --- */
351 switch (f->in.type) {
361 if ((fd = open(f->in.u.name, O_RDONLY | O_NONBLOCK)) < 0) {
362 fw_log(-1, "[%s] couldn't open `%s' for reading: %s",
363 desc, f->in.u.name, strerror(errno));
370 /* --- Make the output file second --- */
372 switch (f->out.type) {
374 out = file_nullref();
381 int m = f->fo.of | O_WRONLY | O_NONBLOCK;
384 /* --- First try creating the file --- *
386 * This lets me know whether to set the file attributes or not. It
387 * also stands a chance of noticing people playing silly beggars with
392 (fd = open(f->out.u.name, m | O_EXCL, f->fa.mode)) < 0 &&
393 (errno != EEXIST || (m & O_EXCL))) {
394 fw_log(-1, "[%s] couldn't create `%s': %s",
395 desc, f->out.u.name, strerror(errno));
401 if (fattr_apply(f->out.u.name, &f->fa)) {
402 fw_log(-1, "[%s] couldn't apply file attributes to `%s': %s",
403 desc, f->out.u.name, strerror(errno));
405 } else if ((fd = open(f->out.u.name, m & ~O_CREAT)) < 0) {
406 fw_log(-1, "[%s] couldn't open `%s': %s",
407 desc, f->out.u.name, strerror(errno));
412 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
413 out = reffd_init(fd);
417 /* --- Set up the endpoint --- */
430 /* --- @destroy@ --- */
432 static void file_destroy(fdata *f)
434 if (f->in.type == FTYPE_NAME)
436 if (f->out.type == FTYPE_NAME)
437 xfree(f->out.u.name);
440 /*----- File source description -------------------------------------------*/
442 /* --- @option@ --- */
444 static int fsource_option(source *s, scanner *sc)
446 fsource *fs = (fsource *)s;
447 return (file_option(fs ? &fs->f : 0, sc));
452 static source *fsource_read(scanner *sc)
456 if (!conf_prefix(sc, "file"))
458 fs = CREATE(fsource);
459 fs->s.ops = &fsource_ops;
461 file_read(&fs->f, sc);
465 /* --- @attach@ --- */
467 static void fsource_destroy(source */*s*/);
469 static void fsource_attach(source *s, scanner *sc, target *t)
471 fsource *fs = (fsource *)s;
474 /* --- Set up the source description string --- */
478 file_desc(&fs->f, &d);
479 dstr_puts(&d, " -> ");
480 dstr_puts(&d, t->desc);
481 fs->s.desc = xstrdup(d.buf);
485 /* --- And then create the endpoints --- */
487 if ((ee = t->ops->create(t, fs->s.desc)) == 0)
489 if ((e = file_endpt(&fs->f, fs->s.desc)) == 0) {
493 endpt_join(e, ee, fs->s.desc);
495 /* --- Dispose of the source and target now --- */
499 fsource_destroy(&fs->s);
502 /* --- @destroy@ --- */
504 static void fsource_destroy(source *s)
506 fsource *fs = (fsource *)s;
508 file_destroy(&fs->f);
512 /* --- File source operations --- */
514 source_ops fsource_ops = {
516 fsource_option, fsource_read, fsource_attach, fsource_destroy
519 /*----- File target description -------------------------------------------*/
521 /* --- @option@ --- */
523 static int ftarget_option(target *t, scanner *sc)
525 ftarget *ft = (ftarget *)t;
526 return (file_option(ft ? &ft->f : 0, sc));
531 static target *ftarget_read(scanner *sc)
536 if (!conf_prefix(sc, "file"))
538 ft = CREATE(ftarget);
539 ft->t.ops = &ftarget_ops;
540 file_read(&ft->f, sc);
541 file_desc(&ft->f, &d);
542 ft->t.desc = xstrdup(d.buf);
547 /* --- @create@ --- */
549 static endpt *ftarget_create(target *t, const char *desc)
551 ftarget *ft = (ftarget *)t;
552 endpt *e = file_endpt(&ft->f, desc);
556 /* --- @destroy@ --- */
558 static void ftarget_destroy(target *t)
560 ftarget *ft = (ftarget *)t;
561 file_destroy(&ft->f);
566 /* --- File target operations --- */
568 target_ops ftarget_ops = {
570 ftarget_option, ftarget_read, 0, ftarget_create, ftarget_destroy
573 /*----- That's all, folks -------------------------------------------------*/