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$ 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 52 static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET; 53 54 static const char *path = NULL; 55 static int unit = G_GATE_UNIT_AUTO; 56 static unsigned flags = 0; 57 static int force = 0; 58 static unsigned sectorsize = 0; 59 static unsigned timeout = G_GATE_TIMEOUT; 60 61 static void 62 usage(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 74 static int 75 g_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 85 static void 86 g_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; 104 once_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 169 static void 170 g_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 196 static void 197 g_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 214 int 215 main(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