fwd.c (fw_log): Report the timezone in log messages.
[fwd] / file.c
CommitLineData
48bb1f76 1/* -*-c-*-
2 *
48bb1f76 3 * File source and target
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
206212ca 8/*----- Licensing notice --------------------------------------------------*
48bb1f76 9 *
9155ea97 10 * This file is part of the `fwd' port forwarder.
48bb1f76 11 *
9155ea97 12 * `fwd' is free software; you can redistribute it and/or modify
48bb1f76 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.
206212ca 16 *
9155ea97 17 * `fwd' is distributed in the hope that it will be useful,
48bb1f76 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.
206212ca 21 *
48bb1f76 22 * You should have received a copy of the GNU General Public License
9155ea97 23 * along with `fwd'; if not, write to the Free Software Foundation,
48bb1f76 24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
9155ea97 27#include "fwd.h"
48bb1f76 28
29/*----- Data structures ---------------------------------------------------*/
30
31/* --- File specification --- */
32
33typedef struct fspec {
34 unsigned type;
35 union {
36 reffd *fd;
37 char *name;
38 } u;
39} fspec;
40
41#define FTYPE_GUESS 0u
42#define FTYPE_FD 1u
43#define FTYPE_NAME 2u
44#define FTYPE_NULL 3u
45
46/* --- File options --- */
47
48typedef struct fopts {
49 unsigned of;
50} fopts;
51
52/* --- File data block --- */
53
54typedef struct fdata {
55 fspec in, out;
56 fattr fa;
57 fopts fo;
58} fdata;
59
60/* --- File source block --- */
61
62typedef struct fsource {
63 source s;
64 fdata f;
206212ca 65} fsource;
48bb1f76 66
67/* --- File target block --- */
68
69typedef struct ftarget {
70 target t;
71 fdata f;
72} ftarget;
73
74/*----- Static variables --------------------------------------------------*/
75
76static fopts file_opts = { O_TRUNC };
77
78static reffd *rstdin, *rstdout;
79static reffd *rnull;
80
81/*----- File endpoint operations ------------------------------------------*/
82
83/* --- @wclose@ --- */
84
85static void fept_wclose(endpt *e)
86{
87 if (e->out) {
88 REFFD_DEC(e->out);
89 e->out = 0;
90 }
91}
92
93/* --- @close@ --- */
94
95static void fept_close(endpt *e)
96{
97 if (e->in)
98 REFFD_DEC(e->in);
99 if (e->out)
100 REFFD_DEC(e->out);
101 fw_dec();
102 DESTROY(e);
103}
104
105/* --- Endpoint operations table --- */
106
268f6af4 107static endpt_ops fept_ops = { 0, 0, fept_wclose, fept_close };
48bb1f76 108
109/*----- General operations on sources and targets -------------------------*/
110
111/* --- @file_null@ --- *
112 *
113 * Arguments: @void *p@ = an uninteresting argument
114 *
115 * Returns: ---
116 *
117 * Use: Removes the reference to @rnull@ when the file closes.
118 */
119
120static void file_null(void *p)
121{
122 rnull = 0;
123}
124
125/* --- @file_nullref@ --- *
126 *
127 * Arguments: ---
128 *
129 * Returns: A reference to a file descriptor open on /dev/null.
130 *
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.
134 */
135
136static reffd *file_nullref(void)
137{
138 if (rnull)
139 REFFD_INC(rnull);
140 else {
141 int n;
142 if ((n = open("/dev/null", O_RDWR)) < 0) {
d935f68b 143 fw_log(NOW, "couldn't open `/dev/null': %s", strerror(errno));
48bb1f76 144 return (0);
145 }
146 fdflags(n, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
147 rnull = reffd_init(n);
148 reffd_handler(rnull, file_null, 0);
149 }
150 return (rnull);
206212ca 151}
48bb1f76 152
153/* --- @file_fspec@ --- *
154 *
155 * Arguments: @fspec *f@ = pointer to filespec to fill in
156 * @scanner *sc@ = pointer to scanner to read from
157 *
158 * Returns: ---
159 *
160 * Use: Reads in a file spec.
161 */
162
163static void file_fspec(fspec *f, scanner *sc)
164{
165 int c = 0;
166 unsigned type = FTYPE_GUESS;
167 reffd *fd = 0;
168
169 /* --- Set up references to @stdin@ and @stdout@ --- */
170
171 if (!rstdin) {
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);
176 }
177
178 /* --- Try to get an access type --- */
179
180 if (sc->t == ':') {
181 c = 1;
182 token(sc);
183 }
184
185 if (sc->t == CTOK_WORD) {
186 int i = conf_enum(sc, "fd,name,null", c ? ENUM_ABBREV : ENUM_NONE,
187 "file access type");
188 type = i + 1;
189 if (type && sc->t == ':')
190 token(sc);
191 }
192
193 /* --- Check for a valid file descriptor name --- */
194
195 if (sc->t == CTOK_WORD && type != FTYPE_NAME && type != FTYPE_NULL) {
196 if (strcmp(sc->d.buf, "stdin") == 0) {
197 fd = rstdin;
198 REFFD_INC(rstdin);
199 } else if (strcmp(sc->d.buf, "stdout") == 0) {
200 fd = rstdout;
201 REFFD_INC(rstdout);
202 }
203 if (fd)
204 REFFD_INC(fd);
205 else if (isdigit((unsigned char)*sc->d.buf)) {
206 int nfd = atoi(sc->d.buf);
207 switch (nfd) {
208 case STDIN_FILENO: fd = rstdin; REFFD_INC(rstdin); break;
209 case STDOUT_FILENO: fd = rstdout; REFFD_INC(rstdout); break;
210 default:
211 fd = reffd_init(nfd);
212 fdflags(nfd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
213 break;
214 }
215 }
216 }
217
218 /* --- Sort out what to do as a result --- */
219
220 if (type == FTYPE_NULL)
221 f->type = FTYPE_NULL;
222 else if (fd) {
223 f->type = FTYPE_FD;
224 f->u.fd = fd;
206212ca 225 token(sc);
48bb1f76 226 } else if (type == FTYPE_FD) {
227 if (sc->t == CTOK_WORD)
228 error(sc, "bad file descriptor `%s'", sc->d.buf);
229 else
230 error(sc, "parse error, expected file descriptor");
231 } else {
232 dstr d = DSTR_INIT;
35a142ca 233 conf_fname(sc, &d);
48bb1f76 234 f->type = FTYPE_NAME;
235 f->u.name = xstrdup(d.buf);
236 dstr_destroy(&d);
237 }
238}
239
240/* --- @read@ --- */
241
242static void file_read(fdata *f, scanner *sc)
243{
244 file_fspec(&f->in, sc);
d105b00f
MW
245 if (sc->t == ',') {
246 token(sc);
247 file_fspec(&f->out, sc);
248 } else {
48bb1f76 249 f->out = f->in;
250 if (f->out.type == FTYPE_FD && f->out.u.fd == rstdin)
251 f->out.u.fd = rstdout;
d105b00f
MW
252 else if (f->out.type == FTYPE_NAME)
253 f->out.u.name = xstrdup(f->in.u.name);
48bb1f76 254 }
255 f->fa = fattr_global;
256 f->fo = file_opts;
257}
258
259/* --- @print@ --- */
260
261static void file_pfspec(fspec *f, dstr *d)
262{
263 switch (f->type) {
264 case FTYPE_FD:
265 switch (f->u.fd->fd) {
266 case STDIN_FILENO:
267 dstr_puts(d, "stdin");
268 break;
269 case STDOUT_FILENO:
270 dstr_puts(d, "stdout");
271 break;
272 default:
273 dstr_putf(d, "fd:%i", f->u.fd->fd);
274 break;
275 }
276 break;
277 case FTYPE_NAME:
278 dstr_putf(d, "name:%s", f->u.name);
279 break;
280 case FTYPE_NULL:
281 dstr_puts(d, "null");
282 break;
206212ca 283 }
48bb1f76 284}
285
286static void file_desc(fdata *f, dstr *d)
287{
288 dstr_puts(d, "file ");
289 file_pfspec(&f->in, d);
290 dstr_puts(d, ", ");
291 file_pfspec(&f->out, d);
292}
293
294/* --- @option@ --- */
295
296static int file_option(fdata *f, scanner *sc)
297{
298 fopts *fo = f ? &f->fo : &file_opts;
299
300 CONF_BEGIN(sc, "file", "file")
301
302 /* --- Handle file-specific options --- */
303
304 if (strcmp(sc->d.buf, "create") == 0) {
305 token(sc);
306 if (sc->t == '=')
307 token(sc);
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;
311 }
312 CONF_ACCEPT;
313 }
314
315 else if (strcmp(sc->d.buf, "open") == 0) {
316 token(sc);
317 if (sc->t == '=')
318 token(sc);
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;
325 }
326 CONF_ACCEPT;
327 }
328
329 /* --- See if it's a file attribute --- */
330
331 {
332 fattr *fa = f ? &f->fa : &fattr_global;
333 if (fattr_option(sc, fa))
334 CONF_ACCEPT;
335 }
336
337 /* --- Nobody understood it --- */
338
339 CONF_END;
340}
341
342/* --- @endpt@ --- */
343
344static endpt *file_endpt(fdata *f, const char *desc)
345{
346 reffd *in = 0, *out = 0;
347 endpt *e;
348
349 /* --- Make the input file first --- */
350
351 switch (f->in.type) {
352 case FTYPE_NULL:
353 in = file_nullref();
354 break;
355 case FTYPE_FD:
356 in = f->in.u.fd;
357 REFFD_INC(in);
358 break;
359 case FTYPE_NAME: {
360 int fd;
361 if ((fd = open(f->in.u.name, O_RDONLY | O_NONBLOCK)) < 0) {
d935f68b 362 fw_log(NOW, "[%s] couldn't open `%s' for reading: %s",
48bb1f76 363 desc, f->in.u.name, strerror(errno));
364 return (0);
365 }
366 in = reffd_init(fd);
367 } break;
368 }
369
370 /* --- Make the output file second --- */
371
372 switch (f->out.type) {
373 case FTYPE_NULL:
374 out = file_nullref();
375 break;
376 case FTYPE_FD:
377 out = f->out.u.fd;
378 REFFD_INC(out);
379 break;
380 case FTYPE_NAME: {
381 int m = f->fo.of | O_WRONLY | O_NONBLOCK;
382 int fd = -1;
383
384 /* --- First try creating the file --- *
385 *
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
388 * dangling symlinks.
389 */
390
391 if ((m & O_CREAT) &&
392 (fd = open(f->out.u.name, m | O_EXCL, f->fa.mode)) < 0 &&
393 (errno != EEXIST || (m & O_EXCL))) {
d935f68b 394 fw_log(NOW, "[%s] couldn't create `%s': %s",
48bb1f76 395 desc, f->out.u.name, strerror(errno));
396 REFFD_DEC(in);
397 return (0);
398 }
399
400 if (fd != -1) {
401 if (fattr_apply(f->out.u.name, &f->fa)) {
d935f68b 402 fw_log(NOW, "[%s] couldn't apply file attributes to `%s': %s",
48bb1f76 403 desc, f->out.u.name, strerror(errno));
404 }
405 } else if ((fd = open(f->out.u.name, m & ~O_CREAT)) < 0) {
d935f68b 406 fw_log(NOW, "[%s] couldn't open `%s': %s",
48bb1f76 407 desc, f->out.u.name, strerror(errno));
408 REFFD_DEC(in);
409 return (0);
410 }
411
412 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
413 out = reffd_init(fd);
414 } break;
415 }
416
417 /* --- Set up the endpoint --- */
418
419 e = CREATE(endpt);
420 e->ops = &fept_ops;
421 e->other = 0;
422 e->f = EPF_FILE;
423 e->t = 0;
424 e->in = in;
425 e->out = out;
426 fw_inc();
427 return (e);
428}
429
430/* --- @destroy@ --- */
431
432static void file_destroy(fdata *f)
433{
434 if (f->in.type == FTYPE_NAME)
b0805b27 435 xfree(f->in.u.name);
48bb1f76 436 if (f->out.type == FTYPE_NAME)
b0805b27 437 xfree(f->out.u.name);
48bb1f76 438}
439
440/*----- File source description -------------------------------------------*/
441
442/* --- @option@ --- */
443
444static int fsource_option(source *s, scanner *sc)
445{
446 fsource *fs = (fsource *)s;
447 return (file_option(fs ? &fs->f : 0, sc));
448}
449
450/* --- @read@ --- */
451
452static source *fsource_read(scanner *sc)
453{
454 fsource *fs;
455
456 if (!conf_prefix(sc, "file"))
457 return (0);
458 fs = CREATE(fsource);
459 fs->s.ops = &fsource_ops;
460 fs->s.desc = 0;
461 file_read(&fs->f, sc);
462 return (&fs->s);
463}
464
465/* --- @attach@ --- */
466
467static void fsource_destroy(source */*s*/);
468
469static void fsource_attach(source *s, scanner *sc, target *t)
470{
471 fsource *fs = (fsource *)s;
472 endpt *e, *ee;
473
474 /* --- Set up the source description string --- */
475
476 {
477 dstr d = DSTR_INIT;
478 file_desc(&fs->f, &d);
479 dstr_puts(&d, " -> ");
480 dstr_puts(&d, t->desc);
481 fs->s.desc = xstrdup(d.buf);
482 dstr_destroy(&d);
483 }
484
485 /* --- And then create the endpoints --- */
486
487 if ((ee = t->ops->create(t, fs->s.desc)) == 0)
488 goto tidy;
489 if ((e = file_endpt(&fs->f, fs->s.desc)) == 0) {
490 ee->ops->close(ee);
491 goto tidy;
492 }
86c9bc4b 493 endpt_join(e, ee, fs->s.desc);
48bb1f76 494
495 /* --- Dispose of the source and target now --- */
496
497tidy:
498 t->ops->destroy(t);
499 fsource_destroy(&fs->s);
500}
501
502/* --- @destroy@ --- */
503
504static void fsource_destroy(source *s)
505{
506 fsource *fs = (fsource *)s;
b0805b27 507 xfree(fs->s.desc);
48bb1f76 508 file_destroy(&fs->f);
509 DESTROY(fs);
510}
511
512/* --- File source operations --- */
513
514source_ops fsource_ops = {
515 "file",
516 fsource_option, fsource_read, fsource_attach, fsource_destroy
517};
518
519/*----- File target description -------------------------------------------*/
520
521/* --- @option@ --- */
522
523static int ftarget_option(target *t, scanner *sc)
524{
525 ftarget *ft = (ftarget *)t;
526 return (file_option(ft ? &ft->f : 0, sc));
527}
528
529/* --- @read@ --- */
530
531static target *ftarget_read(scanner *sc)
532{
533 ftarget *ft;
534 dstr d = DSTR_INIT;
535
536 if (!conf_prefix(sc, "file"))
537 return (0);
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);
543 dstr_destroy(&d);
544 return (&ft->t);
545}
546
547/* --- @create@ --- */
548
549static endpt *ftarget_create(target *t, const char *desc)
550{
551 ftarget *ft = (ftarget *)t;
552 endpt *e = file_endpt(&ft->f, desc);
553 return (e);
554}
555
556/* --- @destroy@ --- */
557
558static void ftarget_destroy(target *t)
559{
560 ftarget *ft = (ftarget *)t;
561 file_destroy(&ft->f);
b0805b27 562 xfree(ft->t.desc);
48bb1f76 563 DESTROY(ft);
564}
565
566/* --- File target operations --- */
567
568target_ops ftarget_ops = {
569 "file",
ee599f55 570 ftarget_option, ftarget_read, 0, ftarget_create, ftarget_destroy
48bb1f76 571};
572
573/*----- That's all, folks -------------------------------------------------*/