Provide a `--pidfile' option in `fw'.
[fwd] / file.c
CommitLineData
48bb1f76 1/* -*-c-*-
2 *
7481fc9c 3 * $Id: file.c,v 1.7 2004/04/08 01:36:25 mdw Exp $
48bb1f76 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
48bb1f76 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
63typedef 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
78typedef struct fopts {
79 unsigned of;
80} fopts;
81
82/* --- File data block --- */
83
84typedef struct fdata {
85 fspec in, out;
86 fattr fa;
87 fopts fo;
88} fdata;
89
90/* --- File source block --- */
91
92typedef struct fsource {
93 source s;
94 fdata f;
95} fsource;
96
97/* --- File target block --- */
98
99typedef struct ftarget {
100 target t;
101 fdata f;
102} ftarget;
103
104/*----- Static variables --------------------------------------------------*/
105
106static fopts file_opts = { O_TRUNC };
107
108static reffd *rstdin, *rstdout;
109static reffd *rnull;
110
111/*----- File endpoint operations ------------------------------------------*/
112
113/* --- @wclose@ --- */
114
115static 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
125static 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
268f6af4 137static endpt_ops fept_ops = { 0, 0, fept_wclose, fept_close };
48bb1f76 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
150static 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
166static 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
193static 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
272static 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
289static 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
314static 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
324static 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
372static 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)) {
59571fdf 430 fw_log(-1, "[%s] couldn't apply file attributes to `%s': %s",
48bb1f76 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
460static void file_destroy(fdata *f)
461{
462 if (f->in.type == FTYPE_NAME)
b0805b27 463 xfree(f->in.u.name);
48bb1f76 464 if (f->out.type == FTYPE_NAME)
b0805b27 465 xfree(f->out.u.name);
48bb1f76 466}
467
468/*----- File source description -------------------------------------------*/
469
470/* --- @option@ --- */
471
472static 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
480static 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
495static void fsource_destroy(source */*s*/);
496
497static 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
525tidy:
526 t->ops->destroy(t);
527 fsource_destroy(&fs->s);
528}
529
530/* --- @destroy@ --- */
531
532static void fsource_destroy(source *s)
533{
534 fsource *fs = (fsource *)s;
b0805b27 535 xfree(fs->s.desc);
48bb1f76 536 file_destroy(&fs->f);
537 DESTROY(fs);
538}
539
540/* --- File source operations --- */
541
542source_ops fsource_ops = {
543 "file",
544 fsource_option, fsource_read, fsource_attach, fsource_destroy
545};
546
547/*----- File target description -------------------------------------------*/
548
549/* --- @option@ --- */
550
551static 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
559static 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
577static 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
586static void ftarget_destroy(target *t)
587{
588 ftarget *ft = (ftarget *)t;
589 file_destroy(&ft->f);
b0805b27 590 xfree(ft->t.desc);
48bb1f76 591 DESTROY(ft);
592}
593
594/* --- File target operations --- */
595
596target_ops ftarget_ops = {
597 "file",
ee599f55 598 ftarget_option, ftarget_read, 0, ftarget_create, ftarget_destroy
48bb1f76 599};
600
601/*----- That's all, folks -------------------------------------------------*/