/* SPDX-License-Identifier: GPL-2.0 */ /** \file file_write.c simple output plugin for testing purposes */ #include #include #include "write_cmd.lsg.h" #include "para.h" #include "list.h" #include "sched.h" #include "buffer_tree.h" #include "write.h" #include "string.h" #include "fd.h" #include "error.h" /** Data specific to the file writer. */ struct private_file_write_data { /** The file descriptor of the output file. */ int fd; }; /* * Get a random filename. * * This is by no means a secure way to create temporary files in a hostile * directory like \p /tmp. However, we use it only for creating temp files in * ~/.paraslash, for which it is OK. Result must be freed by the caller. */ __must_check __malloc static char *random_filename(void) { char *result, *home = para_homedir(); srandom(clock_get_realtime(NULL)->tv_usec); result = make_message("%s/.paraslash/%08ld", home, para_random(99999999)); free(home); return result; } static int prepare_output_file(struct writer_node *wn) { const unsigned flags = O_WRONLY | O_CREAT, mode = S_IRUSR | S_IWUSR; int ret, fd; struct private_file_write_data *pfwd; if (WRITE_CMD_OPT_GIVEN(FILE, FILENAME, wn->lpr)) { const char *path = WRITE_CMD_OPT_STRING_VAL(FILE, FILENAME, wn->lpr); ret = para_open(path, flags, mode); } else { char *path = random_filename(); ret = para_open(path, flags, mode); free(path); } if (ret < 0) return ret; fd = ret; ret = mark_fd_blocking(fd); if (ret < 0) { close(fd); return ret; } pfwd = wn->private_data = zalloc(sizeof(*pfwd)); pfwd->fd = fd; return 1; } static void file_write_pre_monitor(struct sched *s, void *context) { struct writer_node *wn = context; struct private_file_write_data *pfwd = wn->private_data; int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); static bool warned; if (!warned) { PARA_WARNING_LOG("the file writer is deprecated, don't use\n"); warned = true; } if (ret == 0) return; if (ret < 0 || !pfwd) return sched_min_delay(s); sched_monitor_writefd(pfwd->fd, s); } static void file_write_close(struct writer_node *wn) { struct private_file_write_data *pfwd = wn->private_data; if (!pfwd) return; close(pfwd->fd); free(pfwd); } static int file_write_post_monitor(__a_unused struct sched *s, void *context) { struct writer_node *wn = context; struct private_file_write_data *pfwd = wn->private_data; struct btr_node *btrn = wn->btrn; int ret; char *buf; size_t bytes; ret = task_get_notification(wn->task); if (ret < 0) goto out; ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF); if (ret <= 0) goto out; if (!pfwd) { ret = prepare_output_file(wn); goto out; } if (!sched_write_ok(pfwd->fd, s)) return 0; bytes = btr_next_buffer(btrn, &buf); assert(bytes > 0); //PARA_INFO_LOG("writing %zu\n", bytes); ret = xwrite(pfwd->fd, buf, bytes); if (ret < 0) goto out; btr_consume(btrn, ret); out: if (ret < 0) btr_remove_node(&wn->btrn); return ret; } /** \cond doxygen_ignore */ struct writer lsg_write_cmd_com_file_user_data = { .pre_monitor = file_write_pre_monitor, .post_monitor = file_write_post_monitor, .close = file_write_close, }; /** \endcond */