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, ATTACH, CREATE, DESTROY, LIST } action = UNSET; 51 52 static const char *path = NULL; 53 static int unit = -1; 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 attach [-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 void 74 g_gatel_serve(int fd) 75 { 76 struct g_gate_ctl_io ggio; 77 size_t bsize; 78 79 if (g_gate_verbose == 0) { 80 if (daemon(0, 0) < 0) { 81 g_gate_destroy(unit, 1); 82 err(EXIT_FAILURE, "Cannot daemonize"); 83 } 84 } 85 g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid()); 86 ggio.gctl_version = G_GATE_VERSION; 87 ggio.gctl_unit = unit; 88 bsize = sectorsize; 89 ggio.gctl_data = malloc(bsize); 90 for (;;) { 91 int error; 92 once_again: 93 ggio.gctl_length = bsize; 94 ggio.gctl_error = 0; 95 g_gate_ioctl(G_GATE_CMD_START, &ggio); 96 error = ggio.gctl_error; 97 switch (error) { 98 case 0: 99 break; 100 case ECANCELED: 101 /* Exit gracefully. */ 102 free(ggio.gctl_data); 103 g_gate_close_device(); 104 close(fd); 105 exit(EXIT_SUCCESS); 106 case ENOMEM: 107 /* Buffer too small. */ 108 assert(ggio.gctl_cmd == BIO_DELETE || 109 ggio.gctl_cmd == BIO_WRITE); 110 ggio.gctl_data = realloc(ggio.gctl_data, 111 ggio.gctl_length); 112 if (ggio.gctl_data != NULL) { 113 bsize = ggio.gctl_length; 114 goto once_again; 115 } 116 /* FALLTHROUGH */ 117 case ENXIO: 118 default: 119 g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME, 120 strerror(error)); 121 } 122 123 error = 0; 124 switch (ggio.gctl_cmd) { 125 case BIO_READ: 126 if ((size_t)ggio.gctl_length > bsize) { 127 ggio.gctl_data = realloc(ggio.gctl_data, 128 ggio.gctl_length); 129 if (ggio.gctl_data != NULL) 130 bsize = ggio.gctl_length; 131 else 132 error = ENOMEM; 133 } 134 if (error == 0) { 135 if (pread(fd, ggio.gctl_data, ggio.gctl_length, 136 ggio.gctl_offset) == -1) { 137 error = errno; 138 } 139 } 140 break; 141 case BIO_DELETE: 142 case BIO_WRITE: 143 if (pwrite(fd, ggio.gctl_data, ggio.gctl_length, 144 ggio.gctl_offset) == -1) { 145 error = errno; 146 } 147 break; 148 default: 149 error = EOPNOTSUPP; 150 } 151 152 ggio.gctl_error = error; 153 g_gate_ioctl(G_GATE_CMD_DONE, &ggio); 154 } 155 } 156 157 static void 158 g_gatel_create(void) 159 { 160 struct g_gate_ctl_create ggioc; 161 int fd; 162 163 fd = open(path, g_gate_openflags(flags)); 164 if (fd < 0) 165 err(EXIT_FAILURE, "Cannot open %s", path); 166 ggioc.gctl_version = G_GATE_VERSION; 167 ggioc.gctl_unit = unit; 168 ggioc.gctl_mediasize = g_gate_mediasize(fd); 169 if (sectorsize == 0) 170 sectorsize = g_gate_sectorsize(fd); 171 ggioc.gctl_sectorsize = sectorsize; 172 ggioc.gctl_timeout = timeout; 173 ggioc.gctl_flags = flags; 174 ggioc.gctl_maxcount = queue_size; 175 strlcpy(ggioc.gctl_info, path, sizeof(ggioc.gctl_info)); 176 g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc); 177 if (unit == -1) 178 printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit); 179 unit = ggioc.gctl_unit; 180 g_gatel_serve(fd); 181 } 182 183 static void 184 g_gatel_attach(void) 185 { 186 int fd; 187 188 fd = open(path, g_gate_openflags(flags)); 189 if (fd < 0) 190 err(EXIT_FAILURE, "Cannot open %s", path); 191 g_gatel_serve(fd); 192 } 193 194 int 195 main(int argc, char *argv[]) 196 { 197 198 if (argc < 2) 199 usage(); 200 if (strcasecmp(argv[1], "attach") == 0) 201 action = ATTACH; 202 else if (strcasecmp(argv[1], "create") == 0) 203 action = CREATE; 204 else if (strcasecmp(argv[1], "destroy") == 0) 205 action = DESTROY; 206 else if (strcasecmp(argv[1], "list") == 0) 207 action = LIST; 208 else 209 usage(); 210 argc -= 1; 211 argv += 1; 212 for (;;) { 213 int ch; 214 215 ch = getopt(argc, argv, "fo:q:s:t:u:v"); 216 if (ch == -1) 217 break; 218 switch (ch) { 219 case 'f': 220 if (action != DESTROY) 221 usage(); 222 force = 1; 223 break; 224 case 'o': 225 if (action != ATTACH && action != CREATE) 226 usage(); 227 if (strcasecmp("ro", optarg) == 0) 228 flags = G_GATE_FLAG_READONLY; 229 else if (strcasecmp("wo", optarg) == 0) 230 flags = G_GATE_FLAG_WRITEONLY; 231 else if (strcasecmp("rw", optarg) == 0) 232 flags = 0; 233 else { 234 errx(EXIT_FAILURE, 235 "Invalid argument for '-o' option."); 236 } 237 break; 238 case 'q': 239 if (action != CREATE) 240 usage(); 241 errno = 0; 242 queue_size = strtoul(optarg, NULL, 10); 243 if (queue_size == 0 && errno != 0) 244 errx(EXIT_FAILURE, "Invalid queue_size."); 245 break; 246 case 's': 247 if (action != CREATE) 248 usage(); 249 errno = 0; 250 sectorsize = strtoul(optarg, NULL, 10); 251 if (sectorsize == 0 && errno != 0) 252 errx(EXIT_FAILURE, "Invalid sectorsize."); 253 break; 254 case 't': 255 if (action != CREATE) 256 usage(); 257 errno = 0; 258 timeout = strtoul(optarg, NULL, 10); 259 if (timeout == 0 && errno != 0) 260 errx(EXIT_FAILURE, "Invalid timeout."); 261 break; 262 case 'u': 263 errno = 0; 264 unit = strtol(optarg, NULL, 10); 265 if (unit == 0 && errno != 0) 266 errx(EXIT_FAILURE, "Invalid unit number."); 267 break; 268 case 'v': 269 if (action == DESTROY) 270 usage(); 271 g_gate_verbose++; 272 break; 273 default: 274 usage(); 275 } 276 } 277 argc -= optind; 278 argv += optind; 279 280 switch (action) { 281 case ATTACH: 282 if (argc != 1) 283 usage(); 284 if (unit == -1) { 285 fprintf(stderr, "Required unit number.\n"); 286 usage(); 287 } 288 g_gate_open_device(); 289 path = argv[0]; 290 g_gatel_attach(); 291 break; 292 case CREATE: 293 if (argc != 1) 294 usage(); 295 g_gate_load_module(); 296 g_gate_open_device(); 297 path = argv[0]; 298 g_gatel_create(); 299 break; 300 case DESTROY: 301 if (unit == -1) { 302 fprintf(stderr, "Required unit number.\n"); 303 usage(); 304 } 305 g_gate_verbose = 1; 306 g_gate_open_device(); 307 g_gate_destroy(unit, force); 308 break; 309 case LIST: 310 g_gate_list(unit, g_gate_verbose); 311 break; 312 case UNSET: 313 default: 314 usage(); 315 } 316 g_gate_close_device(); 317 exit(EXIT_SUCCESS); 318 } 319