ggatel.c revision 220265
1104476Ssam/*- 2139825Simp * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3104476Ssam * All rights reserved. 4104476Ssam * 5104476Ssam * Redistribution and use in source and binary forms, with or without 6104476Ssam * modification, are permitted provided that the following conditions 7104476Ssam * are met: 8104476Ssam * 1. Redistributions of source code must retain the above copyright 9104476Ssam * notice, this list of conditions and the following disclaimer. 10104476Ssam * 2. Redistributions in binary form must reproduce the above copyright 11104476Ssam * notice, this list of conditions and the following disclaimer in the 12104476Ssam * documentation and/or other materials provided with the distribution. 13104476Ssam * 14104476Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15104476Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16104476Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17104476Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18104476Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19104476Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20104476Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21104476Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22104476Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23104476Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24104476Ssam * SUCH DAMAGE. 25104476Ssam * 26104476Ssam * $FreeBSD: head/sbin/ggate/ggatel/ggatel.c 220265 2011-04-02 06:59:05Z pjd $ 27104476Ssam */ 28104476Ssam 29104476Ssam#include <stdio.h> 30104476Ssam#include <stdlib.h> 31104476Ssam#include <stdint.h> 32104476Ssam#include <fcntl.h> 33104476Ssam#include <unistd.h> 34104476Ssam#include <string.h> 35104476Ssam#include <err.h> 36104476Ssam#include <errno.h> 37104476Ssam#include <assert.h> 38104476Ssam#include <sys/param.h> 39116191Sobrien#include <sys/time.h> 40116191Sobrien#include <sys/bio.h> 41116191Sobrien#include <sys/disk.h> 42104476Ssam#include <sys/ioctl.h> 43104476Ssam#include <sys/stat.h> 44104476Ssam#include <sys/syslog.h> 45104476Ssam 46104476Ssam#include <geom/gate/g_gate.h> 47104476Ssam#include "ggate.h" 48104476Ssam 49104476Ssam 50104476Ssamenum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET; 51104476Ssam 52104476Ssamstatic const char *path = NULL; 53143423Sumestatic int unit = G_GATE_UNIT_AUTO; 54104476Ssamstatic unsigned flags = 0; 55104476Ssamstatic int force = 0; 56104476Ssamstatic unsigned sectorsize = 0; 57104476Ssamstatic unsigned timeout = G_GATE_TIMEOUT; 58104476Ssam 59104476Ssamstatic void 60104476Ssamusage(void) 61104476Ssam{ 62104476Ssam 63104476Ssam fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] " 64104476Ssam "[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname()); 65104476Ssam fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] <-u unit> " 66104476Ssam "<path>\n", getprogname()); 67104476Ssam fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname()); 68104476Ssam fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname()); 69104476Ssam exit(EXIT_FAILURE); 70104476Ssam} 71104476Ssam 72104476Ssamstatic int 73104476Ssamg_gate_openflags(unsigned ggflags) 74104476Ssam{ 75104476Ssam 76104476Ssam if ((ggflags & G_GATE_FLAG_READONLY) != 0) 77104476Ssam return (O_RDONLY); 78104476Ssam else if ((ggflags & G_GATE_FLAG_WRITEONLY) != 0) 79104476Ssam return (O_WRONLY); 80104476Ssam return (O_RDWR); 81104476Ssam} 82104476Ssam 83104476Ssamstatic void 84104476Ssamg_gatel_serve(int fd) 85104476Ssam{ 86104476Ssam struct g_gate_ctl_io ggio; 87104476Ssam size_t bsize; 88104476Ssam 89104476Ssam if (g_gate_verbose == 0) { 90104476Ssam if (daemon(0, 0) == -1) { 91104476Ssam g_gate_destroy(unit, 1); 92104476Ssam err(EXIT_FAILURE, "Cannot daemonize"); 93104476Ssam } 94104476Ssam } 95104476Ssam g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid()); 96104476Ssam ggio.gctl_version = G_GATE_VERSION; 97104476Ssam ggio.gctl_unit = unit; 98104476Ssam bsize = sectorsize; 99104476Ssam ggio.gctl_data = malloc(bsize); 100104476Ssam for (;;) { 101104476Ssam int error; 102104476Ssamonce_again: 103104476Ssam ggio.gctl_length = bsize; 104104476Ssam ggio.gctl_error = 0; 105104476Ssam g_gate_ioctl(G_GATE_CMD_START, &ggio); 106104476Ssam error = ggio.gctl_error; 107104476Ssam switch (error) { 108104476Ssam case 0: 109104476Ssam break; 110104476Ssam case ECANCELED: 111104476Ssam /* Exit gracefully. */ 112104476Ssam free(ggio.gctl_data); 113104476Ssam g_gate_close_device(); 114104476Ssam close(fd); 115104476Ssam exit(EXIT_SUCCESS); 116104476Ssam case ENOMEM: 117104476Ssam /* Buffer too small. */ 118104476Ssam assert(ggio.gctl_cmd == BIO_DELETE || 119104476Ssam ggio.gctl_cmd == BIO_WRITE); 120104476Ssam ggio.gctl_data = realloc(ggio.gctl_data, 121104476Ssam ggio.gctl_length); 122104476Ssam if (ggio.gctl_data != NULL) { 123104476Ssam bsize = ggio.gctl_length; 124104476Ssam goto once_again; 125104476Ssam } 126104476Ssam /* FALLTHROUGH */ 127104476Ssam case ENXIO: 128104476Ssam default: 129104476Ssam g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME, 130104476Ssam strerror(error)); 131104476Ssam } 132104476Ssam 133104476Ssam error = 0; 134104476Ssam switch (ggio.gctl_cmd) { 135104476Ssam case BIO_READ: 136104476Ssam if ((size_t)ggio.gctl_length > bsize) { 137104476Ssam ggio.gctl_data = realloc(ggio.gctl_data, 138104476Ssam ggio.gctl_length); 139104476Ssam if (ggio.gctl_data != NULL) 140104476Ssam bsize = ggio.gctl_length; 141104476Ssam else 142104476Ssam error = ENOMEM; 143104476Ssam } 144104476Ssam if (error == 0) { 145104476Ssam if (pread(fd, ggio.gctl_data, ggio.gctl_length, 146104476Ssam ggio.gctl_offset) == -1) { 147104476Ssam error = errno; 148104476Ssam } 149104476Ssam } 150104476Ssam break; 151104476Ssam case BIO_DELETE: 152104476Ssam case BIO_WRITE: 153104476Ssam if (pwrite(fd, ggio.gctl_data, ggio.gctl_length, 154104476Ssam ggio.gctl_offset) == -1) { 155104476Ssam error = errno; 156104476Ssam } 157104476Ssam break; 158104476Ssam default: 159104476Ssam error = EOPNOTSUPP; 160104476Ssam } 161104476Ssam 162104476Ssam ggio.gctl_error = error; 163104476Ssam g_gate_ioctl(G_GATE_CMD_DONE, &ggio); 164104476Ssam } 165104476Ssam} 166104476Ssam 167104476Ssamstatic void 168104476Ssamg_gatel_create(void) 169104476Ssam{ 170104476Ssam struct g_gate_ctl_create ggioc; 171104476Ssam int fd; 172104476Ssam 173104476Ssam fd = open(path, g_gate_openflags(flags) | O_DIRECT | O_FSYNC); 174104476Ssam if (fd == -1) 175104476Ssam err(EXIT_FAILURE, "Cannot open %s", path); 176104476Ssam ggioc.gctl_version = G_GATE_VERSION; 177104476Ssam ggioc.gctl_unit = unit; 178104476Ssam ggioc.gctl_mediasize = g_gate_mediasize(fd); 179104476Ssam if (sectorsize == 0) 180104476Ssam sectorsize = g_gate_sectorsize(fd); 181104476Ssam ggioc.gctl_sectorsize = sectorsize; 182104476Ssam ggioc.gctl_timeout = timeout; 183104476Ssam ggioc.gctl_flags = flags; 184104476Ssam ggioc.gctl_maxcount = 0; 185104476Ssam strlcpy(ggioc.gctl_info, path, sizeof(ggioc.gctl_info)); 186104476Ssam g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc); 187104476Ssam if (unit == -1) 188104476Ssam printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit); 189104476Ssam unit = ggioc.gctl_unit; 190104476Ssam g_gatel_serve(fd); 191104476Ssam} 192104476Ssam 193104476Ssamstatic void 194104476Ssamg_gatel_rescue(void) 195104476Ssam{ 196104476Ssam struct g_gate_ctl_cancel ggioc; 197104476Ssam int fd; 198104476Ssam 199104476Ssam fd = open(path, g_gate_openflags(flags)); 200104476Ssam if (fd == -1) 201104476Ssam err(EXIT_FAILURE, "Cannot open %s", path); 202104476Ssam 203104476Ssam ggioc.gctl_version = G_GATE_VERSION; 204104476Ssam ggioc.gctl_unit = unit; 205104476Ssam ggioc.gctl_seq = 0; 206104476Ssam g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc); 207104476Ssam 208104476Ssam g_gatel_serve(fd); 209104476Ssam} 210104476Ssam 211104476Ssamint 212104476Ssammain(int argc, char *argv[]) 213104476Ssam{ 214104476Ssam 215104476Ssam if (argc < 2) 216104476Ssam usage(); 217104476Ssam if (strcasecmp(argv[1], "create") == 0) 218104476Ssam action = CREATE; 219104476Ssam else if (strcasecmp(argv[1], "rescue") == 0) 220104476Ssam action = RESCUE; 221104476Ssam else if (strcasecmp(argv[1], "destroy") == 0) 222104476Ssam action = DESTROY; 223104476Ssam else if (strcasecmp(argv[1], "list") == 0) 224104476Ssam action = LIST; 225104476Ssam else 226104476Ssam usage(); 227104476Ssam argc -= 1; 228104476Ssam argv += 1; 229104476Ssam for (;;) { 230104476Ssam int ch; 231104476Ssam 232104476Ssam ch = getopt(argc, argv, "fo:s:t:u:v"); 233104476Ssam if (ch == -1) 234104476Ssam break; 235104476Ssam switch (ch) { 236104476Ssam case 'f': 237104476Ssam if (action != DESTROY) 238104476Ssam usage(); 239104476Ssam force = 1; 240104476Ssam break; 241104476Ssam case 'o': 242104476Ssam if (action != CREATE && action != RESCUE) 243104476Ssam usage(); 244104476Ssam if (strcasecmp("ro", optarg) == 0) 245104476Ssam flags = G_GATE_FLAG_READONLY; 246104476Ssam else if (strcasecmp("wo", optarg) == 0) 247104476Ssam flags = G_GATE_FLAG_WRITEONLY; 248104476Ssam else if (strcasecmp("rw", optarg) == 0) 249104476Ssam flags = 0; 250104476Ssam else { 251104476Ssam errx(EXIT_FAILURE, 252104476Ssam "Invalid argument for '-o' option."); 253104476Ssam } 254104476Ssam break; 255104476Ssam case 's': 256104476Ssam if (action != CREATE) 257104476Ssam usage(); 258104476Ssam errno = 0; 259104476Ssam sectorsize = strtoul(optarg, NULL, 10); 260104476Ssam if (sectorsize == 0 && errno != 0) 261104476Ssam errx(EXIT_FAILURE, "Invalid sectorsize."); 262104476Ssam break; 263104476Ssam case 't': 264104476Ssam if (action != CREATE) 265104476Ssam usage(); 266104476Ssam errno = 0; 267104476Ssam timeout = strtoul(optarg, NULL, 10); 268104476Ssam if (timeout == 0 && errno != 0) 269104476Ssam errx(EXIT_FAILURE, "Invalid timeout."); 270104476Ssam break; 271104476Ssam case 'u': 272104476Ssam errno = 0; 273104476Ssam unit = strtol(optarg, NULL, 10); 274104476Ssam if (unit == 0 && errno != 0) 275104476Ssam errx(EXIT_FAILURE, "Invalid unit number."); 276104476Ssam break; 277104476Ssam case 'v': 278104476Ssam if (action == DESTROY) 279104476Ssam usage(); 280104476Ssam g_gate_verbose++; 281104476Ssam break; 282104476Ssam default: 283104476Ssam usage(); 284104476Ssam } 285104476Ssam } 286104476Ssam argc -= optind; 287104476Ssam argv += optind; 288104476Ssam 289104476Ssam switch (action) { 290104476Ssam case CREATE: 291104476Ssam if (argc != 1) 292104476Ssam usage(); 293104476Ssam g_gate_load_module(); 294104476Ssam g_gate_open_device(); 295104476Ssam path = argv[0]; 296104476Ssam g_gatel_create(); 297104476Ssam break; 298104476Ssam case RESCUE: 299104476Ssam if (argc != 1) 300104476Ssam usage(); 301104476Ssam if (unit == -1) { 302104476Ssam fprintf(stderr, "Required unit number.\n"); 303104476Ssam usage(); 304104476Ssam } 305104476Ssam g_gate_open_device(); 306104476Ssam path = argv[0]; 307104476Ssam g_gatel_rescue(); 308104476Ssam break; 309104476Ssam case DESTROY: 310104476Ssam if (unit == -1) { 311104476Ssam fprintf(stderr, "Required unit number.\n"); 312104476Ssam usage(); 313104476Ssam } 314104476Ssam g_gate_verbose = 1; 315104476Ssam g_gate_open_device(); 316104476Ssam g_gate_destroy(unit, force); 317104476Ssam break; 318104476Ssam case LIST: 319104476Ssam g_gate_list(unit, g_gate_verbose); 320104476Ssam break; 321104476Ssam case UNSET: 322104476Ssam default: 323104476Ssam usage(); 324104476Ssam } 325104476Ssam g_gate_close_device(); 326104476Ssam exit(EXIT_SUCCESS); 327104476Ssam} 328104476Ssam