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