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