summaryrefslogtreecommitdiff
path: root/file_write.c
blob: b666642d76c230a9c4e30a009afd8808a69e3297 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* SPDX-License-Identifier: GPL-2.0 */

/** \file file_write.c simple output plugin for testing purposes */

#include <sys/types.h>
#include <lopsub.h>

#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 */