1/* This file is part of Mailfromd. -*- c -*- 2 Copyright (C) 2006-2021 Sergey Poznyakoff 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 3, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17MF_BUILTIN_MODULE 18 19#include <mflib/status.h> 20#include <sys/types.h> 21#include <sys/stat.h> 22#include <sys/wait.h> 23#include "global.h" 24#include "msg.h" 25 26static size_t nstreams = MAX_IOSTREAMS; 27 28static struct mu_cfg_param io_cfg_param[] = { 29 { "max-streams", mu_c_size, &nstreams, 0, NULL, 30 N_("Maximum number of stream descriptors.") }, 31 { NULL } 32}; 33 34struct io_stream { 35 char *name; 36 mu_locker_t lock; 37 int fd[2]; 38 pid_t pid; 39 char *buf; 40 size_t bufsize; 41 int (*shutdown)(struct io_stream *, int what); 42 void (*cleanup)(void*); 43 void *cleanup_data; 44 char *delim; 45}; 46 47#define IFD(s) ((s).fd[0]) 48#define OFD(s) ((s).fd[1] == -1 ? (s).fd[0] : (s).fd[1]) 49 50static void 51flush_stream(struct io_stream *str) 52{ 53 /*FIXME*/ 54} 55 56static void 57close_stream(struct io_stream *str) 58{ 59 if (OFD(*str) == -1) 60 return; 61 flush_stream(str); 62 close(OFD(*str)); 63 if (IFD(*str) != -1) 64 close(IFD(*str)); 65 if (str->pid) { 66 int status; 67 waitpid(str->pid, &status, 0); 68 } 69 str->fd[0] = -1; 70 str->fd[1] = -1; 71 str->pid = 0; 72 if (str->cleanup) 73 str->cleanup(str->cleanup_data); 74 str->cleanup = NULL; 75 str->cleanup_data = NULL; 76 if (str->name) { 77 free(str->name); 78 str->name = NULL; 79 } 80 if (str->delim) { 81 free(str->delim); 82 str->delim = NULL; 83 } 84} 85 86/* Read bytes from the stream STR into its buffer, until 87 DELIM is encountered. Return number of bytes read. */ 88static int 89read_stream_delim(struct io_stream *str, char *delim) 90{ 91 int fd = IFD(*str); 92 size_t i = 0; 93 int rc; 94 size_t delim_len = strlen(delim); 95 96 for (;;) { 97 if (str->bufsize == i) { 98 if (str->bufsize == 0) 99 str->bufsize = 16; 100 str->buf = mu_2nrealloc(str->buf, &str->bufsize, 101 sizeof str->buf[1]); 102 } 103 rc = read(fd, str->buf + i, 1); 104 if (rc == -1) 105 return -1; 106 else if (rc == 0) 107 return 0; 108 i++; 109 if (i >= delim_len && 110 memcmp(str->buf + i - delim_len, delim, delim_len) == 0) { 111 str->buf[i - delim_len] = 0; 112 break; 113 } 114 } 115 return i; 116} 117 118#define REDIRECT_STDIN_P(f) ((f) & (O_WRONLY|O_RDWR)) 119#define REDIRECT_STDOUT_P(f) (!((f) & O_WRONLY)) 120 121#define STDERR_SHUT 0 122#define STDERR_NULL 1 123#define STDERR_LOG 2 124#define STDERR_FILE 3 125#define STDERR_FILE_APPEND 4 126 127#define LOG_TAG_PFX "mailfromd:" 128#define LOG_TAG_PFX_LEN (sizeof(LOG_TAG_PFX)-1) 129 130static void 131stderr_to_log(char *arg, const char *cmd) 132{ 133 int p[2]; 134 pid_t pid; 135 136 if (pipe(p)) { 137 mu_error(_("pipe failed: %s"), mu_strerror(errno)); 138 close(2); 139 return; 140 } 141 142 pid = fork(); 143 144 if (pid == (pid_t) -1) { 145 mu_error(_("fork failed: %s"), mu_strerror(errno)); 146 close(p[0]); 147 close(p[1]); 148 close(2); 149 return; 150 } 151 152 /* Child */ 153 if (pid == 0) { 154 FILE *fp; 155 fd_set fdset; 156 size_t len; 157 char buf[1024]; 158 char *tag; 159 int fac = mu_log_facility, pri = LOG_ERR; 160 161 if (arg) { 162 char *p = strchr(arg, '.'); 163 164 if (p) 165 *p++ = 0; 166 if (mu_string_to_syslog_facility(arg, &fac)) { 167 mu_error(_("unknown syslog facility (%s), " 168 "redirecting stderr to %s"), 169 arg, 170 mu_syslog_facility_to_string(fac)); 171 } 172 173 if (p && mu_string_to_syslog_priority(p, &pri)) { 174 mu_error(_("unknown syslog priority (%s), " 175 "redirecting stderr to %s"), 176 arg, 177 mu_syslog_priority_to_string(pri)); 178 } 179 } 180 MF_DEBUG(MU_DEBUG_TRACE2, 181 ("redirecting stderr to syslog %s.%s", 182 mu_syslog_facility_to_string(fac), 183 mu_syslog_priority_to_string(pri))); 184 185 len = strcspn(cmd, " \t"); 186 tag = malloc(LOG_TAG_PFX_LEN + len + 1); 187 if (!tag) 188 tag = (char*) cmd; 189 else { 190 strcpy(tag, LOG_TAG_PFX); 191 memcpy(tag + LOG_TAG_PFX_LEN, cmd, len); 192 tag[LOG_TAG_PFX_LEN + len] = 0; 193 } 194 mf_proctitle_format("%s redirector", cmd); 195 196 FD_ZERO(&fdset); 197 FD_SET(p[0], &fdset); 198 logger_fdset(&fdset); 199 close_fds_except(&fdset); 200 201 fp = fdopen(p[0], "r"); 202 logger_open(); 203 while (fgets(buf, sizeof(buf), fp)) 204 syslog(pri, "%s", buf); 205 exit(0); 206 } 207 208 /* Parent */ 209 close(p[0]); 210 dup2(p[1], 2); 211 close(p[1]); 212} 213 214static void 215stderr_handler(int mode, char *arg, const char *cmd) 216{ 217 int fd; 218 int append = O_TRUNC; 219 220 switch (mode) { 221 case STDERR_SHUT: 222 close(2); 223 break; 224 225 case STDERR_NULL: 226 arg = "/dev/null"; 227 case STDERR_FILE_APPEND: 228 append = O_APPEND; 229 case STDERR_FILE: 230 if (!arg || !*arg) { 231 close(2); 232 break; 233 } 234 MF_DEBUG(MU_DEBUG_TRACE2, ("redirecting stderr to %s", arg)); 235 fd = open(arg, O_CREAT|O_WRONLY|append, 0644); 236 if (fd < 0) { 237 mu_error(_("cannot open file %s for appending: %s"), 238 arg, mu_strerror(errno)); 239 close(2); 240 return; 241 } 242 if (fd != 2) { 243 dup2(fd, 2); 244 close(fd); 245 } 246 break; 247 248 case STDERR_LOG: 249 stderr_to_log(arg, cmd); 250 } 251} 252 253static void 254parse_stderr_redirect(const char **pcmd, int *perr, char **parg) 255{ 256 int err; 257 size_t len; 258 char *arg; 259 const char *cmdline = *pcmd; 260 261 while (*cmdline && mu_isspace(*cmdline)) 262 cmdline++; 263 if (strncmp(cmdline, "2>file:", 7) == 0) { 264 cmdline += 7; 265 err = STDERR_FILE; 266 } else if (strncmp(cmdline, "2>>file:", 8) == 0) { 267 cmdline += 8; 268 err = STDERR_FILE_APPEND; 269 } else if (strncmp(cmdline, "2>null:", 7) == 0) { 270 cmdline += 7; 271 err = STDERR_NULL; 272 } else if (strncmp(cmdline, "2>syslog:", 9) == 0) { 273 cmdline += 9; 274 err = STDERR_LOG; 275 } else 276 return; 277 278 len = strcspn(cmdline, " \t"); 279 if (len > 0 && cmdline[len-1] == 0) 280 return; 281 if (len == 0) 282 arg = NULL; 283 else { 284 arg = malloc(len + 1); 285 if (!arg) 286 return; 287 memcpy(arg, cmdline, len); 288 arg[len] = 0; 289 } 290 291 *pcmd = cmdline + len; 292 *perr = err; 293 *parg = arg; 294} 295 296 297static int 298open_program_stream_ioe(eval_environ_t env, 299 struct io_stream *str, const char *cmdline, 300 int flags, 301 int ioe[2]) 302{ 303 int rightp[2], leftp[2]; 304 int rc = 0; 305 pid_t pid; 306 int err = STDERR_SHUT; 307 char *arg = NULL; 308 struct mu_wordsplit ws; 309 310 parse_stderr_redirect(&cmdline, &err, &arg); 311 while (*cmdline && (*cmdline == ' ' || *cmdline == '\t')) 312 cmdline++; 313 314 if (REDIRECT_STDIN_P(flags)) { 315 if (pipe(leftp)) { 316 mu_diag_funcall(MU_DIAG_ERROR, "pipe", "leftp", 317 errno); 318 free(arg); 319 MF_THROW(mfe_failure, "pipe failed"); 320 } 321 } 322 323 if (REDIRECT_STDOUT_P(flags)) { 324 if (pipe(rightp)) { 325 mu_diag_funcall(MU_DIAG_ERROR, "pipe", "rightp", 326 errno); 327 free(arg); 328 if (REDIRECT_STDIN_P(flags)) { 329 close(leftp[0]); 330 close(leftp[1]); 331 } 332 } 333 } 334 335 switch (pid = fork()) { 336 /* The child branch. */ 337 case 0: 338 /* attach the pipes */ 339 340 /* Right-end */ 341 if (REDIRECT_STDOUT_P(flags)) { 342 if (rightp[1] != 1) 343 dup2(rightp[1], 1); 344 } else if (ioe && ioe[1] != -1 && ioe[1] != 1) { 345 dup2(ioe[1], 1); 346 } 347 348 /* Left-end */ 349 if (REDIRECT_STDIN_P(flags)) { 350 if (leftp[0] != 0) 351 dup2(leftp[0], 0); 352 } else if (ioe && ioe[0] != -1 && ioe[0] != 0) { 353 dup2(ioe[0], 0); 354 } 355 356 if (ioe && ioe[2] != -1 && ioe[2] != 2) 357 dup2(ioe[2], 2); 358 else 359 stderr_handler(err, arg, cmdline); 360 361 /* Close unneeded descriptors */ 362 close_fds_above(2); 363 364 MF_DEBUG(MU_DEBUG_TRACE3, ("running %s", cmdline)); 365 if (mu_wordsplit(cmdline, &ws, 366 MU_WRDSF_DEFFLAGS & ~MU_WRDSF_CESCAPES)) { 367 mu_error(_("cannot parse command line %s: %s"), 368 cmdline, mu_wordsplit_strerror(&ws)); 369 exit(127); 370 } 371 execvp(ws.ws_wordv[0], ws.ws_wordv); 372 mu_error(_("cannot run %s: %s"), 373 cmdline, mu_strerror(errno)); 374 exit(127); 375 /********************/ 376 377 /* Parent branches: */ 378 case -1: 379 /* Fork has failed */ 380 /* Restore things */ 381 rc = errno; 382 if (REDIRECT_STDOUT_P(flags)) { 383 close(rightp[0]); 384 close(rightp[1]); 385 } 386 if (REDIRECT_STDIN_P(flags)) { 387 close(leftp[0]); 388 close(leftp[1]); 389 } 390 break; 391 392 default: 393 str->pid = pid; 394 if (REDIRECT_STDOUT_P(flags)) { 395 str->fd[0] = rightp[0]; 396 close(rightp[1]); 397 } else 398 str->fd[0] = -1; 399 400 if (REDIRECT_STDIN_P(flags)) { 401 str->fd[1] = leftp[1]; 402 close(leftp[0]); 403 } else 404 str->fd[1] = -1; 405 } 406 free(arg); 407 return rc; 408} 409 410static int 411open_program_stream(eval_environ_t env, 412 struct io_stream *str, const char *cmdline, 413 int flags) 414{ 415 return open_program_stream_ioe(env, str, cmdline, flags, NULL); 416} 417 418static int 419open_file_stream(eval_environ_t env, 420 struct io_stream *str, const char *file, int flags) 421{ 422 str->fd[0] = open(file, flags, 0644); /* FIXME: mode? */ 423 if (str->fd[0] == -1) 424 return errno; 425 return 0; 426} 427 428 429 430static int 431open_parsed_inet_stream(eval_environ_t env, 432 struct io_stream *str, 433 const char *cstr, 434 char *proto, char *port, char *path, 435 int flags) 436{ 437 union { 438 struct sockaddr sa; 439 struct sockaddr_in s_in; 440 struct sockaddr_un s_un; 441#ifdef GACOPYZ_IPV6 442 struct sockaddr_in6 s_in6; 443#endif 444 } addr; 445 446 socklen_t socklen; 447 int fd; 448 int rc; 449 450 if (!proto 451 || strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0) { 452 struct stat st; 453 454 MF_ASSERT(port == NULL, 455 mfe_failure, 456 _("invalid connection type: %s; " 457 "port is meaningless for UNIX sockets"), 458 cstr); 459 460 MF_ASSERT(strlen(path) <= sizeof addr.s_un.sun_path, 461 mfe_range, 462 _("%s: UNIX socket name too long"), 463 path); 464 465 addr.sa.sa_family = PF_UNIX; 466 socklen = sizeof(addr.s_un); 467 strcpy(addr.s_un.sun_path, path); 468 469 if (stat(path, &st)) { 470 MF_THROW(mfe_failure, 471 _("%s: cannot stat socket: %s"), 472 path, strerror(errno)); 473 } else { 474 /* FIXME: Check permissions? */ 475 MF_ASSERT(S_ISSOCK(st.st_mode), 476 mfe_failure, 477 _("%s: not a socket"), 478 path); 479 } 480 481 } else if (strcmp(proto, "inet") == 0) { 482 short pnum; 483 long num; 484 char *p; 485 486 addr.sa.sa_family = PF_INET; 487 socklen = sizeof(addr.s_in); 488 489 MF_ASSERT(port != NULL, 490 mfe_failure, 491 _("invalid connection type: %s; " 492 "missing port number"), 493 cstr); 494 495 num = pnum = strtol(port, &p, 0); 496 if (*p == 0) { 497 MF_ASSERT(num == pnum, 498 mfe_range, 499 _("invalid connection type: " 500 "%s; bad port number"), 501 cstr); 502 pnum = htons(pnum); 503 } else { 504 struct servent *sp = getservbyname(port, "tcp"); 505 506 MF_ASSERT(sp != NULL, 507 mfe_failure, 508 _("invalid connection type: " 509 "%s; unknown port name"), 510 cstr); 511 pnum = sp->s_port; 512 } 513 514 if (!path) 515 addr.s_in.sin_addr.s_addr = INADDR_ANY; 516 else { 517 struct hostent *hp = gethostbyname(path); 518 MF_ASSERT(hp != NULL, 519 mfe_failure, 520 _("unknown host name %s"), 521 path); 522 addr.sa.sa_family = hp->h_addrtype; 523 switch (hp->h_addrtype) { 524 case AF_INET: 525 memmove(&addr.s_in.sin_addr, hp->h_addr, 4); 526 addr.s_in.sin_port = pnum; 527 break; 528 529 default: 530 MF_THROW(mfe_range, 531 _("invalid connection type: " 532 "%s; unsupported address family"), 533 cstr); 534 } 535 } 536#ifdef GACOPYZ_IPV6 537 } else if (strcmp(proto, "inet6") == 0) { 538 struct addrinfo hints; 539 struct addrinfo *res; 540 541 MF_ASSERT(port != NULL, 542 mfe_failure, 543 _("invalid connection type: %s; " 544 "missing port number"), 545 cstr); 546 547 memset(&hints, 0, sizeof(hints)); 548 hints.ai_family = AF_INET6; 549 hints.ai_socktype = SOCK_STREAM; 550 if (!path) 551 hints.ai_flags |= AI_PASSIVE; 552 553 rc = getaddrinfo(path, port, &hints, &res); 554 555 switch (rc) { 556 case 0: 557 break; 558 559 case EAI_SYSTEM: 560 MF_THROW(mfe_failure, 561 _("%s:%s: cannot parse address: %s"), 562 path, port, strerror(errno)); 563 564 case EAI_BADFLAGS: 565 case EAI_SOCKTYPE: 566 MF_THROW(mfe_failure, 567 _("%s:%d: internal error converting %s:%s"), 568 __FILE__, __LINE__, path, port); 569 570 case EAI_MEMORY: 571 mu_alloc_die(); 572 573 default: 574 MF_THROW(mfe_failure, 575 "%s:%s: %s", 576 path, port, gai_strerror(rc)); 577 } 578 579 socklen = res->ai_addrlen; 580 if (socklen > sizeof(addr)) { 581 freeaddrinfo(res); 582 MF_THROW(mfe_failure, 583 _("%s:%s: address length too big (%lu)"), 584 path, port, 585 (unsigned long) socklen); 586 } 587 memcpy(&addr, res->ai_addr, res->ai_addrlen); 588 freeaddrinfo(res); 589#endif 590 } else { 591 MF_THROW(mfe_range, 592 _("unsupported protocol: %s"), 593 proto); 594 } 595 596 fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); 597 MF_ASSERT(fd != -1, 598 mfe_failure, 599 _("unable to create new socket: %s"), 600 strerror(errno)); 601 602 /* FIXME: Bind to the source ? */ 603 604 rc = connect(fd, &addr.sa, socklen); 605 if (rc) { 606 close(fd); 607 MF_THROW(mfe_failure, 608 _("cannot connect to %s: %s"), 609 cstr, strerror(errno)); 610 } 611 612 str->fd[0] = fd; 613 return 0; 614} 615 616static int 617shutdown_inet_stream(struct io_stream *str, int how) 618{ 619 switch (how) { 620 case 0: 621 how = SHUT_RD; 622 break; 623 624 case 1: 625 how = SHUT_WR; 626 break; 627 628 case 2: 629 how = SHUT_RDWR; 630 break; 631 632 default: 633 return EINVAL; 634 } 635 if (shutdown(str->fd[0], how)) 636 return errno; 637 return 0; 638} 639 640static int 641open_inet_stream(eval_environ_t env, 642 struct io_stream *str, const char *addr, int flags) 643{ 644 int rc; 645 char *proto, *port, *path; 646 647 if (gacopyz_parse_connection(addr, &proto, &port, &path) 648 != MI_SUCCESS) 649 rc = ENOMEM; /* FIXME: or EINVAL? */ 650 else { 651 rc = open_parsed_inet_stream(env, 652 str, addr, 653 proto, port, path, flags); 654 str->shutdown = shutdown_inet_stream; 655 free(proto); 656 free(port); 657 free(path); 658 } 659 return rc; 660} 661 662 663static void * 664alloc_streams() 665{ 666 struct io_stream *p, *stab = mu_calloc(nstreams, sizeof *stab); 667 for (p = stab; p < stab + nstreams; p++) 668 p->fd[0] = p->fd[1] = -1; 669 return stab; 670} 671 672static void 673destroy_streams(void *data) 674{ 675 struct io_stream *stab = data; 676 struct io_stream *p; 677 for (p = stab; p < stab + nstreams; p++) { 678 close_stream(p); 679 free(p->buf); 680 } 681 free(stab); 682} 683 684MF_DECLARE_DATA(IO, alloc_streams, destroy_streams) 685 686int 687_bi_io_fd(eval_environ_t env, int fd, int what) 688{ 689 struct io_stream *iotab = MF_GET_DATA; 690 int descr; 691 692 MF_ASSERT(fd >= 0 && fd < nstreams && what>=0 && what<=1, 693 mfe_range, 694 _("invalid file descriptor")); 695 descr = what == 0 ? IFD(iotab[fd]) : OFD(iotab[fd]); 696 MF_ASSERT(descr >= 0, 697 mfe_range, 698 _("invalid file descriptor")); 699 return descr; 700} 701 702 703MF_DEFUN(open, NUMBER, STRING name) 704{ 705 int i, rc; 706 int flags = 0; 707 int (*opf)(eval_environ_t env, 708 struct io_stream *, const char *, int) = open_file_stream; 709 struct io_stream *iotab = MF_GET_DATA; 710 711 for (i = 0; i < nstreams; i++) { 712 if (iotab[i].fd[0] == -1) 713 break; 714 } 715 MF_ASSERT(i < nstreams, 716 mfe_failure, 717 _("no more files available")); 718 719 MF_DEBUG(MU_DEBUG_TRACE1, ("opening stream %s", name)); 720 iotab[i].name = mu_strdup(name); 721 iotab[i].delim = NULL; 722 if (*name == '>') { 723 flags |= O_RDWR|O_CREAT; 724 name++; 725 if (*name == '>') { 726 flags |= O_APPEND; 727 name++; 728 } else 729 flags |= O_TRUNC; 730 } else if (*name == '|') { 731 opf = open_program_stream; 732 flags = O_WRONLY; 733 name++; 734 if (*name == '&') { 735 flags = O_RDWR; 736 name++; 737 } else if (*name == '<') { 738 flags = O_RDONLY; 739 name++; 740 } 741 } else if (*name == '@') { 742 name++; 743 opf = open_inet_stream; 744 flags = O_RDWR; 745 } else 746 flags = O_RDONLY; 747 748 for (;*name && mu_isspace(*name); name++) 749 ; 750 751 rc = opf(env, &iotab[i], name, flags); 752 753 MF_ASSERT(rc == 0, 754 mfe_failure, 755 _("cannot open stream %s: %s"), name, 756 mu_strerror(rc)); 757 MF_DEBUG(MU_DEBUG_TRACE1, ("open(%s) = %d", name, i)); 758 MF_RETURN(i); 759} 760END 761 762MF_DEFUN(spawn, NUMBER, STRING name, OPTIONAL, 763 NUMBER fin, NUMBER fout, NUMBER ferr) 764{ 765 int i, rc; 766 struct io_stream *iotab = MF_GET_DATA; 767 int ioe[3]; 768 int flags; 769 770 for (i = 0; i < nstreams; i++) { 771 if (iotab[i].fd[0] == -1) 772 break; 773 } 774 MF_ASSERT(i < nstreams, 775 mfe_failure, 776 _("no more files available")); 777 778 MF_DEBUG(MU_DEBUG_TRACE1, ("spawning %s", name)); 779 iotab[i].name = mu_strdup(name); 780 iotab[i].delim = NULL; 781 782 flags = O_WRONLY; 783 if (*name == '|') 784 name++; 785 if (*name == '&') { 786 flags = O_RDWR; 787 name++; 788 } else if (*name == '<') { 789 flags = O_RDONLY; 790 name++; 791 } 792 793 for (;*name && mu_isspace(*name); name++) 794 ; 795 796 if (MF_DEFINED(fin)) 797 ioe[0] = _bi_io_fd(env, MF_OPTVAL(fin), 0); 798 else 799 ioe[0] = -1; 800 if (MF_DEFINED(fout)) 801 ioe[1] = _bi_io_fd(env, MF_OPTVAL(fout), 1); 802 else 803 ioe[1] = -1; 804 if (MF_DEFINED(ferr)) 805 ioe[2] = _bi_io_fd(env, MF_OPTVAL(fout), 1); 806 else 807 ioe[2] = -1; 808 809 rc = open_program_stream_ioe(env, &iotab[i], name, flags, ioe); 810 811 MF_ASSERT(rc == 0, 812 mfe_failure, 813 _("cannot open stream %s: %s"), name, 814 mu_strerror(rc)); 815 MF_DEBUG(MU_DEBUG_TRACE1, ("spawn(%s) = %d", name, i)); 816 MF_RETURN(i); 817 818} 819END 820 821MF_DSEXP 822MF_DEFUN(tempfile, NUMBER, OPTIONAL, STRING tempdir) 823{ 824 struct io_stream *iotab = MF_GET_DATA; 825 int i; 826 char *dir = MF_OPTVAL(tempdir, "/tmp"); 827 size_t dirlen = strlen(dir); 828 mode_t u; 829 int fd; 830 char *template; 831#define PATTERN "mfdXXXXXX" 832 833 for (i = 0; i < nstreams; i++) { 834 if (iotab[i].fd[0] == -1) 835 break; 836 } 837 MF_ASSERT(i < nstreams, 838 mfe_failure, 839 _("no more files available")); 840 841 842 while (dirlen > 0 && dir[dirlen-1] == '/') 843 dirlen--; 844 845 template = MF_ALLOC_HEAP_TEMP((dirlen ? dirlen + 1 : 0) + 846 sizeof(PATTERN)); 847 if (dirlen) { 848 memcpy(template, dir, dirlen); 849 template[dirlen++] = '/'; 850 } 851 strcpy(template + dirlen, PATTERN); 852 u = umask(077); 853 fd = mkstemp(template); 854 umask(u); 855 MF_ASSERT(fd >= 0, 856 mfe_failure, 857 "mkstemp failed: %s", 858 mu_strerror(errno)); 859 unlink(template); 860 861 iotab[i].fd[0] = fd; 862 863 MF_RETURN(i); 864#undef PATTERN 865} 866END 867 868MF_DEFUN(close, VOID, NUMBER fd) 869{ 870 struct io_stream *iotab = MF_GET_DATA; 871 872 MF_ASSERT(fd >= 0 && fd < nstreams, 873 mfe_range, 874 _("invalid file descriptor")); 875 close_stream(&iotab[fd]); 876} 877END 878 879static struct builtin_const_trans shutdown_modes[] = { 880 MF_TRANS(SHUT_RD), 881 MF_TRANS(SHUT_WR), 882 MF_TRANS(SHUT_RDWR) 883}; 884 885MF_DEFUN(shutdown, VOID, NUMBER fd, NUMBER how) 886{ 887 struct io_stream *iotab = MF_GET_DATA; 888 struct io_stream *ioptr; 889 int mode; 890 891 MF_ASSERT(fd >= 0 && fd < nstreams, 892 mfe_range, 893 _("invalid file descriptor")); 894 MF_ASSERT(how >= 0 && how <= 2, 895 mfe_range, 896 _("invalid file descriptor")); 897 MF_ASSERT(_builtin_const_to_c(shutdown_modes, 898 MU_ARRAY_SIZE(shutdown_modes), 899 how, 900 &mode) == 0, 901 mfe_failure, 902 "bad shutdown mode"); 903 904 ioptr = &iotab[fd]; 905 if (ioptr->shutdown) { 906 int rc = ioptr->shutdown(ioptr, mode); 907 MF_ASSERT(rc == 0, 908 mfe_io, 909 "shutdown failed: %s", 910 mu_strerror(rc)); 911 } else if (how == 2) 912 close_stream(ioptr); 913 else if (ioptr->fd[how]) { 914 close(ioptr->fd[how]); 915 ioptr->fd[how] = -1; 916 } 917} 918END 919 920MF_DEFUN(write, VOID, NUMBER fd, STRING str, OPTIONAL, NUMBER n) 921{ 922 struct io_stream *iotab = MF_GET_DATA; 923 int rc; 924 925 MF_DEBUG(MU_DEBUG_TRACE1, ("writing %s to %lu", str, fd)); 926 MF_ASSERT(fd >= 0 && fd < nstreams && OFD(iotab[fd]) != -1, 927 mfe_range, 928 _("invalid file descriptor")); 929 if (!MF_DEFINED(n)) 930 n = strlen (str); 931 rc = write(OFD(iotab[fd]), str, n); 932 MF_ASSERT(n == rc, 933 mfe_io, 934 _("write error on %s: %s"), 935 iotab[fd].name, mu_strerror(errno)); 936} 937END 938 939MF_STATE(body) 940MF_DEFUN(write_body, VOID, NUMBER fd, POINTER str, NUMBER n) 941{ 942 struct io_stream *iotab = MF_GET_DATA; 943 int rc; 944 945 MF_ASSERT(fd >= 0 && fd < nstreams && OFD(iotab[fd]) != -1, 946 mfe_range, 947 _("invalid file descriptor")); 948 rc = write(OFD(iotab[fd]), str, n); 949 MF_ASSERT(n == rc, 950 mfe_io, 951 _("write error on %s: %s"), 952 iotab[fd].name, mu_strerror(errno)); 953} 954END 955 956MF_DEFUN(read, STRING, NUMBER fd, NUMBER size) 957{ 958 struct io_stream *iotab = MF_GET_DATA; 959 int rc; 960 size_t off; 961 char *s = MF_ALLOC_HEAP(off, size + 1); 962 963 MF_ASSERT(fd >= 0 && fd < nstreams && IFD(iotab[fd]) != -1, 964 mfe_range, 965 _("invalid file descriptor")); 966 967 rc = read(IFD(iotab[fd]), s, size); 968 if (rc == 0) 969 MF_THROW(mfe_eof, 970 _("EOF on %s"), iotab[fd].name); 971 MF_ASSERT(rc == size, 972 mfe_io, 973 _("read error on %s: %s"), 974 iotab[fd].name, mu_strerror(errno)); 975 s[size] = 0; 976 MF_RETURN(off, size); 977} 978END 979 980MF_DEFUN(rewind, VOID, NUMBER fd) 981{ 982 struct io_stream *iotab = MF_GET_DATA; 983 984 MF_ASSERT(fd >= 0 && fd < nstreams && IFD(iotab[fd]) != -1, 985 mfe_range, 986 _("invalid file descriptor")); 987 if (lseek(IFD(iotab[fd]), 0, SEEK_SET) == -1) 988 MF_THROW(mfe_io, 989 "seek failed: %s", 990 mu_strerror(errno)); 991} 992END 993 994 995#define MINBUFSIZE 128 996#define MAXBUFSIZE 65535 997 998MF_DEFUN(copy, NUMBER, NUMBER dst, NUMBER src) 999{ 1000 struct io_stream *iotab = MF_GET_DATA; 1001 int ifd, ofd; 1002 char *buffer; 1003 size_t bufsize = MAXBUFSIZE; 1004 char bs[MINBUFSIZE]; 1005 off_t cur, end; 1006 size_t total = 0; 1007 ssize_t rdbytes; 1008 1009 MF_ASSERT(src >= 0 && src < nstreams && (ifd = IFD(iotab[src])) != -1, 1010 mfe_range, 1011 _("invalid source file descriptor")); 1012 MF_ASSERT(dst >= 0 && dst < nstreams && (ofd = OFD(iotab[dst])) != -1, 1013 mfe_range, 1014 _("invalid destination file descriptor")); 1015 1016 cur = lseek (ifd, 0, SEEK_CUR); 1017 if (cur != -1) { 1018 end = lseek (ifd, 0, SEEK_END); 1019 if (end != -1) { 1020 if (end < MAXBUFSIZE) 1021 bufsize = end; 1022 lseek (ifd, cur, SEEK_SET); 1023 } 1024 } 1025 1026 for (; (buffer = malloc (bufsize)) == NULL; bufsize >>= 1) 1027 if (bufsize < MINBUFSIZE) { 1028 buffer = bs; 1029 bufsize = MINBUFSIZE; 1030 break; 1031 } 1032 1033 while ((rdbytes = read(ifd, buffer, bufsize)) > 0) { 1034 char *p = buffer; 1035 while (rdbytes) { 1036 ssize_t wrbytes = write(ofd, p, rdbytes); 1037 if (wrbytes == -1) { 1038 if (buffer != bs) 1039 free(buffer); 1040 MF_THROW(mfe_io, 1041 "write error: %s", 1042 mu_strerror(errno)); 1043 } else if (wrbytes == 0) { 1044 if (buffer != bs) 1045 free(buffer); 1046 MF_THROW(mfe_io, 1047 "short write"); 1048 } 1049 p += wrbytes; 1050 rdbytes -= wrbytes; 1051 total += wrbytes; 1052 } 1053 } 1054 if (buffer != bs) 1055 free(buffer); 1056 MF_RETURN(total); 1057} 1058END 1059 1060MF_DEFUN(getdelim, STRING, NUMBER fd, STRING delim) 1061{ 1062 struct io_stream *iotab = MF_GET_DATA; 1063 struct io_stream *ioptr; 1064 int rc; 1065 1066 MF_ASSERT(fd >= 0 && fd < nstreams && IFD(iotab[fd]) != -1, 1067 mfe_range, 1068 _("invalid file descriptor")); 1069 ioptr = &iotab[fd]; 1070 rc = read_stream_delim(ioptr, delim); 1071 if (rc == 0) 1072 MF_THROW(mfe_eof, _("EOF on %s"), ioptr->name); 1073 MF_ASSERT(rc > 0, 1074 mfe_io, 1075 _("read error on %s: %s"), 1076 ioptr->name, mu_strerror(errno)); 1077 MF_RETURN(ioptr->buf); 1078} 1079END 1080 1081MF_DEFUN(getline, STRING, NUMBER fd) 1082{ 1083 struct io_stream *iotab = MF_GET_DATA; 1084 struct io_stream *ioptr; 1085 int rc; 1086 1087 MF_ASSERT(fd >= 0 && fd < nstreams && IFD(iotab[fd]) != -1, 1088 mfe_range, 1089 _("invalid file descriptor")); 1090 ioptr = &iotab[fd]; 1091 rc = read_stream_delim(ioptr, ioptr->delim ? ioptr->delim : "\n"); 1092 if (rc == 0) 1093 MF_THROW(mfe_eof, 1094 _("EOF on %s"), ioptr->name); 1095 MF_ASSERT(rc > 0, 1096 mfe_io, 1097 _("read error on %s: %s"), 1098 ioptr->name, mu_strerror(errno)); 1099 MF_RETURN(ioptr->buf); 1100} 1101END 1102 1103MF_DEFUN(fd_set_delimiter, VOID, NUMBER fd, STRING delim) 1104{ 1105 struct io_stream *iotab = MF_GET_DATA; 1106 struct io_stream *ioptr; 1107 1108 MF_ASSERT(fd >= 0 && fd < nstreams && IFD(iotab[fd]) != -1, 1109 mfe_range, 1110 _("invalid file descriptor")); 1111 ioptr = &iotab[fd]; 1112 free(ioptr->delim); 1113 ioptr->delim = mu_strdup(delim); 1114} 1115END 1116 1117MF_DEFUN(fd_delimiter, STRING, NUMBER fd, STRING delim) 1118{ 1119 struct io_stream *iotab = MF_GET_DATA; 1120 struct io_stream *ioptr; 1121 1122 MF_ASSERT(fd >= 0 && fd < nstreams && IFD(iotab[fd]) != -1, 1123 mfe_range, 1124 _("invalid file descriptor")); 1125 ioptr = &iotab[fd]; 1126 MF_RETURN(ioptr->delim ? ioptr->delim : "\n"); 1127} 1128END 1129 1130MF_INIT([< 1131 mf_add_runtime_params(io_cfg_param); 1132 >]) 1133