ggatel.c revision 330449
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: stable/11/sbin/ggate/ggatel/ggatel.c 330449 2018-03-05 07:26:05Z eadler $
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <stdint.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <string.h>
37#include <err.h>
38#include <errno.h>
39#include <assert.h>
40#include <sys/param.h>
41#include <sys/time.h>
42#include <sys/bio.h>
43#include <sys/disk.h>
44#include <sys/ioctl.h>
45#include <sys/stat.h>
46#include <sys/syslog.h>
47
48#include <geom/gate/g_gate.h>
49#include "ggate.h"
50
51
52static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
53
54static const char *path = NULL;
55static int unit = G_GATE_UNIT_AUTO;
56static unsigned flags = 0;
57static int force = 0;
58static unsigned sectorsize = 0;
59static unsigned timeout = G_GATE_TIMEOUT;
60
61static void
62usage(void)
63{
64
65	fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] "
66	    "[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname());
67	fprintf(stderr, "       %s rescue [-v] [-o <ro|wo|rw>] <-u unit> "
68	    "<path>\n", getprogname());
69	fprintf(stderr, "       %s destroy [-f] <-u unit>\n", getprogname());
70	fprintf(stderr, "       %s list [-v] [-u unit]\n", getprogname());
71	exit(EXIT_FAILURE);
72}
73
74static int
75g_gate_openflags(unsigned ggflags)
76{
77
78	if ((ggflags & G_GATE_FLAG_READONLY) != 0)
79		return (O_RDONLY);
80	else if ((ggflags & G_GATE_FLAG_WRITEONLY) != 0)
81		return (O_WRONLY);
82	return (O_RDWR);
83}
84
85static void
86g_gatel_serve(int fd)
87{
88	struct g_gate_ctl_io ggio;
89	size_t bsize;
90
91	if (g_gate_verbose == 0) {
92		if (daemon(0, 0) == -1) {
93			g_gate_destroy(unit, 1);
94			err(EXIT_FAILURE, "Cannot daemonize");
95		}
96	}
97	g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid());
98	ggio.gctl_version = G_GATE_VERSION;
99	ggio.gctl_unit = unit;
100	bsize = sectorsize;
101	ggio.gctl_data = malloc(bsize);
102	for (;;) {
103		int error;
104once_again:
105		ggio.gctl_length = bsize;
106		ggio.gctl_error = 0;
107		g_gate_ioctl(G_GATE_CMD_START, &ggio);
108		error = ggio.gctl_error;
109		switch (error) {
110		case 0:
111			break;
112		case ECANCELED:
113			/* Exit gracefully. */
114			free(ggio.gctl_data);
115			g_gate_close_device();
116			close(fd);
117			exit(EXIT_SUCCESS);
118		case ENOMEM:
119			/* Buffer too small. */
120			assert(ggio.gctl_cmd == BIO_DELETE ||
121			    ggio.gctl_cmd == BIO_WRITE);
122			ggio.gctl_data = realloc(ggio.gctl_data,
123			    ggio.gctl_length);
124			if (ggio.gctl_data != NULL) {
125				bsize = ggio.gctl_length;
126				goto once_again;
127			}
128			/* FALLTHROUGH */
129		case ENXIO:
130		default:
131			g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
132			    strerror(error));
133		}
134
135		error = 0;
136		switch (ggio.gctl_cmd) {
137		case BIO_READ:
138			if ((size_t)ggio.gctl_length > bsize) {
139				ggio.gctl_data = realloc(ggio.gctl_data,
140				    ggio.gctl_length);
141				if (ggio.gctl_data != NULL)
142					bsize = ggio.gctl_length;
143				else
144					error = ENOMEM;
145			}
146			if (error == 0) {
147				if (pread(fd, ggio.gctl_data, ggio.gctl_length,
148				    ggio.gctl_offset) == -1) {
149					error = errno;
150				}
151			}
152			break;
153		case BIO_DELETE:
154		case BIO_WRITE:
155			if (pwrite(fd, ggio.gctl_data, ggio.gctl_length,
156			    ggio.gctl_offset) == -1) {
157				error = errno;
158			}
159			break;
160		default:
161			error = EOPNOTSUPP;
162		}
163
164		ggio.gctl_error = error;
165		g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
166	}
167}
168
169static void
170g_gatel_create(void)
171{
172	struct g_gate_ctl_create ggioc;
173	int fd;
174
175	fd = open(path, g_gate_openflags(flags) | O_DIRECT | O_FSYNC);
176	if (fd == -1)
177		err(EXIT_FAILURE, "Cannot open %s", path);
178	memset(&ggioc, 0, sizeof(ggioc));
179	ggioc.gctl_version = G_GATE_VERSION;
180	ggioc.gctl_unit = unit;
181	ggioc.gctl_mediasize = g_gate_mediasize(fd);
182	if (sectorsize == 0)
183		sectorsize = g_gate_sectorsize(fd);
184	ggioc.gctl_sectorsize = sectorsize;
185	ggioc.gctl_timeout = timeout;
186	ggioc.gctl_flags = flags;
187	ggioc.gctl_maxcount = 0;
188	strlcpy(ggioc.gctl_info, path, sizeof(ggioc.gctl_info));
189	g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
190	if (unit == -1)
191		printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
192	unit = ggioc.gctl_unit;
193	g_gatel_serve(fd);
194}
195
196static void
197g_gatel_rescue(void)
198{
199	struct g_gate_ctl_cancel ggioc;
200	int fd;
201
202	fd = open(path, g_gate_openflags(flags));
203	if (fd == -1)
204		err(EXIT_FAILURE, "Cannot open %s", path);
205
206	ggioc.gctl_version = G_GATE_VERSION;
207	ggioc.gctl_unit = unit;
208	ggioc.gctl_seq = 0;
209	g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
210
211	g_gatel_serve(fd);
212}
213
214int
215main(int argc, char *argv[])
216{
217
218	if (argc < 2)
219		usage();
220	if (strcasecmp(argv[1], "create") == 0)
221		action = CREATE;
222	else if (strcasecmp(argv[1], "rescue") == 0)
223		action = RESCUE;
224	else if (strcasecmp(argv[1], "destroy") == 0)
225		action = DESTROY;
226	else if (strcasecmp(argv[1], "list") == 0)
227		action = LIST;
228	else
229		usage();
230	argc -= 1;
231	argv += 1;
232	for (;;) {
233		int ch;
234
235		ch = getopt(argc, argv, "fo:s:t:u:v");
236		if (ch == -1)
237			break;
238		switch (ch) {
239		case 'f':
240			if (action != DESTROY)
241				usage();
242			force = 1;
243			break;
244		case 'o':
245			if (action != CREATE && action != RESCUE)
246				usage();
247			if (strcasecmp("ro", optarg) == 0)
248				flags = G_GATE_FLAG_READONLY;
249			else if (strcasecmp("wo", optarg) == 0)
250				flags = G_GATE_FLAG_WRITEONLY;
251			else if (strcasecmp("rw", optarg) == 0)
252				flags = 0;
253			else {
254				errx(EXIT_FAILURE,
255				    "Invalid argument for '-o' option.");
256			}
257			break;
258		case 's':
259			if (action != CREATE)
260				usage();
261			errno = 0;
262			sectorsize = strtoul(optarg, NULL, 10);
263			if (sectorsize == 0 && errno != 0)
264				errx(EXIT_FAILURE, "Invalid sectorsize.");
265			break;
266		case 't':
267			if (action != CREATE)
268				usage();
269			errno = 0;
270			timeout = strtoul(optarg, NULL, 10);
271			if (timeout == 0 && errno != 0)
272				errx(EXIT_FAILURE, "Invalid timeout.");
273			break;
274		case 'u':
275			errno = 0;
276			unit = strtol(optarg, NULL, 10);
277			if (unit == 0 && errno != 0)
278				errx(EXIT_FAILURE, "Invalid unit number.");
279			break;
280		case 'v':
281			if (action == DESTROY)
282				usage();
283			g_gate_verbose++;
284			break;
285		default:
286			usage();
287		}
288	}
289	argc -= optind;
290	argv += optind;
291
292	switch (action) {
293	case CREATE:
294		if (argc != 1)
295			usage();
296		g_gate_load_module();
297		g_gate_open_device();
298		path = argv[0];
299		g_gatel_create();
300		break;
301	case RESCUE:
302		if (argc != 1)
303			usage();
304		if (unit == -1) {
305			fprintf(stderr, "Required unit number.\n");
306			usage();
307		}
308		g_gate_open_device();
309		path = argv[0];
310		g_gatel_rescue();
311		break;
312	case DESTROY:
313		if (unit == -1) {
314			fprintf(stderr, "Required unit number.\n");
315			usage();
316		}
317		g_gate_verbose = 1;
318		g_gate_open_device();
319		g_gate_destroy(unit, force);
320		break;
321	case LIST:
322		g_gate_list(unit, g_gate_verbose);
323		break;
324	case UNSET:
325	default:
326		usage();
327	}
328	g_gate_close_device();
329	exit(EXIT_SUCCESS);
330}
331