1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <stdint.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <err.h> 36 #include <errno.h> 37 #include <assert.h> 38 #include <sys/param.h> 39 #include <sys/time.h> 40 #include <sys/bio.h> 41 #include <sys/disk.h> 42 #include <sys/ioctl.h> 43 #include <sys/stat.h> 44 #include <sys/syslog.h> 45 46 #include "ggate.h" 47 48 49 static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET; 50 51 static const char *path = NULL; 52 static int unit = G_GATE_UNIT_AUTO; 53 static unsigned flags = 0; 54 static int direct_flag = 0; 55 static int force = 0; 56 static unsigned sectorsize = 0; 57 static unsigned timeout = G_GATE_TIMEOUT; 58 59 static void 60 usage(void) 61 { 62 63 fprintf(stderr, "usage: %s create [-v] [-o option] ... " 64 "[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname()); 65 fprintf(stderr, " %s rescue [-v] [-o option] ... <-u unit> " 66 "<path>\n", getprogname()); 67 fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname()); 68 fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname()); 69 fprintf(stderr, " option = {ro, wo, rw, direct}\n"); 70 exit(EXIT_FAILURE); 71 } 72 73 static int 74 g_gate_openflags(unsigned ggflags) 75 { 76 int openflags = O_RDWR; 77 78 if ((ggflags & G_GATE_FLAG_READONLY) != 0) 79 openflags = O_RDONLY; 80 else if ((ggflags & G_GATE_FLAG_WRITEONLY) != 0) 81 openflags = O_WRONLY; 82 83 if (direct_flag) 84 openflags |= O_DIRECT; 85 86 return (openflags); 87 } 88 89 static void 90 g_gatel_serve(int fd) 91 { 92 struct g_gate_ctl_io ggio; 93 size_t bsize; 94 95 if (g_gate_verbose == 0) { 96 if (daemon(0, 0) == -1) { 97 g_gate_destroy(unit, 1); 98 err(EXIT_FAILURE, "Cannot daemonize"); 99 } 100 } 101 g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid()); 102 ggio.gctl_version = G_GATE_VERSION; 103 ggio.gctl_unit = unit; 104 bsize = sectorsize; 105 ggio.gctl_data = malloc(bsize); 106 for (;;) { 107 int error; 108 once_again: 109 ggio.gctl_length = bsize; 110 ggio.gctl_error = 0; 111 g_gate_ioctl(G_GATE_CMD_START, &ggio); 112 error = ggio.gctl_error; 113 switch (error) { 114 case 0: 115 break; 116 case ECANCELED: 117 /* Exit gracefully. */ 118 free(ggio.gctl_data); 119 g_gate_close_device(); 120 close(fd); 121 exit(EXIT_SUCCESS); 122 case ENOMEM: 123 /* Buffer too small. */ 124 assert(ggio.gctl_cmd == BIO_DELETE || 125 ggio.gctl_cmd == BIO_WRITE); 126 ggio.gctl_data = realloc(ggio.gctl_data, 127 ggio.gctl_length); 128 if (ggio.gctl_data != NULL) { 129 bsize = ggio.gctl_length; 130 goto once_again; 131 } 132 /* FALLTHROUGH */ 133 case ENXIO: 134 default: 135 g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME, 136 strerror(error)); 137 } 138 139 error = 0; 140 switch (ggio.gctl_cmd) { 141 case BIO_READ: 142 if ((size_t)ggio.gctl_length > bsize) { 143 ggio.gctl_data = realloc(ggio.gctl_data, 144 ggio.gctl_length); 145 if (ggio.gctl_data != NULL) 146 bsize = ggio.gctl_length; 147 else 148 error = ENOMEM; 149 } 150 if (error == 0) { 151 if (pread(fd, ggio.gctl_data, ggio.gctl_length, 152 ggio.gctl_offset) == -1) { 153 error = errno; 154 } 155 } 156 break; 157 case BIO_DELETE: 158 case BIO_WRITE: 159 if (pwrite(fd, ggio.gctl_data, ggio.gctl_length, 160 ggio.gctl_offset) == -1) { 161 error = errno; 162 } 163 break; 164 default: 165 error = EOPNOTSUPP; 166 } 167 168 ggio.gctl_error = error; 169 g_gate_ioctl(G_GATE_CMD_DONE, &ggio); 170 } 171 } 172 173 static void 174 g_gatel_create(void) 175 { 176 struct g_gate_ctl_create ggioc; 177 int fd; 178 179 fd = open(path, g_gate_openflags(flags) | O_DIRECT | O_FSYNC); 180 if (fd == -1) 181 err(EXIT_FAILURE, "Cannot open %s", path); 182 memset(&ggioc, 0, sizeof(ggioc)); 183 ggioc.gctl_version = G_GATE_VERSION; 184 ggioc.gctl_unit = unit; 185 ggioc.gctl_mediasize = g_gate_mediasize(fd); 186 if (sectorsize == 0) 187 sectorsize = g_gate_sectorsize(fd); 188 ggioc.gctl_sectorsize = sectorsize; 189 ggioc.gctl_timeout = timeout; 190 ggioc.gctl_flags = flags; 191 ggioc.gctl_maxcount = 0; 192 strlcpy(ggioc.gctl_info, path, sizeof(ggioc.gctl_info)); 193 g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc); 194 if (unit == -1) 195 printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit); 196 unit = ggioc.gctl_unit; 197 g_gatel_serve(fd); 198 } 199 200 static void 201 g_gatel_rescue(void) 202 { 203 struct g_gate_ctl_cancel ggioc; 204 int fd; 205 206 fd = open(path, g_gate_openflags(flags)); 207 if (fd == -1) 208 err(EXIT_FAILURE, "Cannot open %s", path); 209 210 ggioc.gctl_version = G_GATE_VERSION; 211 ggioc.gctl_unit = unit; 212 ggioc.gctl_seq = 0; 213 g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc); 214 215 g_gatel_serve(fd); 216 } 217 218 int 219 main(int argc, char *argv[]) 220 { 221 222 if (argc < 2) 223 usage(); 224 if (strcasecmp(argv[1], "create") == 0) 225 action = CREATE; 226 else if (strcasecmp(argv[1], "rescue") == 0) 227 action = RESCUE; 228 else if (strcasecmp(argv[1], "destroy") == 0) 229 action = DESTROY; 230 else if (strcasecmp(argv[1], "list") == 0) 231 action = LIST; 232 else 233 usage(); 234 argc -= 1; 235 argv += 1; 236 for (;;) { 237 int ch; 238 239 ch = getopt(argc, argv, "fo:s:t:u:v"); 240 if (ch == -1) 241 break; 242 switch (ch) { 243 case 'f': 244 if (action != DESTROY) 245 usage(); 246 force = 1; 247 break; 248 case 'o': 249 if (action != CREATE && action != RESCUE) 250 usage(); 251 if (strcasecmp("ro", optarg) == 0) 252 flags = G_GATE_FLAG_READONLY; 253 else if (strcasecmp("wo", optarg) == 0) 254 flags = G_GATE_FLAG_WRITEONLY; 255 else if (strcasecmp("rw", optarg) == 0) 256 flags = 0; 257 else if (strcasecmp("direct", optarg) == 0) 258 direct_flag = 1; 259 else { 260 errx(EXIT_FAILURE, 261 "Invalid argument for '-o' option."); 262 } 263 break; 264 case 's': 265 if (action != CREATE) 266 usage(); 267 errno = 0; 268 sectorsize = strtoul(optarg, NULL, 10); 269 if (sectorsize == 0 && errno != 0) 270 errx(EXIT_FAILURE, "Invalid sectorsize."); 271 break; 272 case 't': 273 if (action != CREATE) 274 usage(); 275 errno = 0; 276 timeout = strtoul(optarg, NULL, 10); 277 if (timeout == 0 && errno != 0) 278 errx(EXIT_FAILURE, "Invalid timeout."); 279 break; 280 case 'u': 281 errno = 0; 282 unit = strtol(optarg, NULL, 10); 283 if (unit == 0 && errno != 0) 284 errx(EXIT_FAILURE, "Invalid unit number."); 285 break; 286 case 'v': 287 if (action == DESTROY) 288 usage(); 289 g_gate_verbose++; 290 break; 291 default: 292 usage(); 293 } 294 } 295 argc -= optind; 296 argv += optind; 297 298 switch (action) { 299 case CREATE: 300 if (argc != 1) 301 usage(); 302 g_gate_load_module(); 303 g_gate_open_device(); 304 path = argv[0]; 305 g_gatel_create(); 306 break; 307 case RESCUE: 308 if (argc != 1) 309 usage(); 310 if (unit == -1) { 311 fprintf(stderr, "Required unit number.\n"); 312 usage(); 313 } 314 g_gate_open_device(); 315 path = argv[0]; 316 g_gatel_rescue(); 317 break; 318 case DESTROY: 319 if (unit == -1) { 320 fprintf(stderr, "Required unit number.\n"); 321 usage(); 322 } 323 g_gate_verbose = 1; 324 g_gate_open_device(); 325 g_gate_destroy(unit, force); 326 break; 327 case LIST: 328 g_gate_list(unit, g_gate_verbose); 329 break; 330 case UNSET: 331 default: 332 usage(); 333 } 334 g_gate_close_device(); 335 exit(EXIT_SUCCESS); 336 } 337