/* * * Copyright 2000-2001 Sistina Software, Inc. * Portions Copyright 2001 The OpenGFS Project * * This is free software released under the GNU General Public License. * There is no warranty for this software. See the file COPYING for * details. * * See the file AUTHORS for a list of contributors. * */ /* * This is a user level interface for setting up and configuring the * kgnbd kernel server. Soon it will also be able to interface the * gnbdd user level server. */ /* * AUDIT: * - Fixed multiple local possible buffer overruns, nothing serious * - Fixed malloc checks */ #include #include #include #include #include #include #include #include #include #include #include int kernel_server; int fd; int read_fd; int message_flag = 1; #define printm(fmt, args...)\ {\ if(message_flag != 0) \ fprintf(stderr, "gserv: " fmt, ##args); \ } size_t gserv_read(void *buf, size_t count) { if (kernel_server) return read(fd, buf, count); return read(read_fd, buf, count); } size_t gserv_write(const void *buf, size_t count) { return write(fd, buf, count); } int get_fd(int flags) { fd = open("/proc/kgnbd", flags); if (fd < 0 && errno != ENOENT) { fprintf(stderr, "could not open /proc/kgnbd: %s\n", strerror(errno)); return -1; } if (fd >= 0) { kernel_server = 1; return 0; } if ((flags & O_ACCMODE) == O_WRONLY) { fd = open("/dev/gnbd_rdpipe", flags); if (fd < 0) { fprintf(stderr, "couldn't connect to gnbd_rdpipe: %s\n", strerror(errno)); return -1; } } if ((flags & O_ACCMODE) == O_RDONLY) { read_fd = open("/dev/gnbd_wrpipe", flags); if (read_fd < 0) { fprintf(stderr, "couldn't connect to gnbd_wrpipe: %s\n", strerror(errno)); return -1; } } kernel_server = 0; return 0; } int servcreate(char *filename, char *pathname) { int len = strlen(filename) + strlen(pathname) + 9; char *buf = (char *) malloc(len * sizeof(char)); if(buf == NULL) exit(1); snprintf(buf, len, "create/%s/%s", filename, pathname); if (write(fd, buf, len) != len) { fprintf(stderr, "create request failed: %s\n", strerror(errno)); free(buf); return 1; } free(buf); printm("created GNBD %s serving file %s\n", filename, pathname); return 0; } int servremove(char *filename) { int len = strlen(filename) + 8; char *buf = (char *) malloc(len * sizeof(char)); if(!buf) exit(1); snprintf(buf, len, "remove/%s", filename); if (gserv_write(buf, len) != len) { fprintf(stderr, "remove request failed: %s\n", strerror(errno)); free(buf); return 1; } free(buf); printm("removed GNBD %s\n", filename); return 0; } int removeall(void) { int bytes, total = 0; char *serv, *bufptr; char buf[4096]; while ((bytes = gserv_read(buf + total, 4096 - total)) != 0) { if (bytes < 0 && errno != -EINTR) { fprintf(stderr, "remove request failed: %s\n", strerror(errno)); return 1; } total += bytes; } if (total < 4096) buf[total] = 0; else buf[total] = 0; bufptr = buf; while ((serv = strchr(bufptr, '/')) != NULL) { serv++; bufptr = strchr(serv, '/'); *bufptr++ = 0; bufptr = strstr(bufptr, "blocksize"); servremove(serv); if (bufptr == NULL) { fprintf(stderr, "incorrectly formatted serverlist\n"); return 1; } } return 0; } int portstop(void) { char *buf = "portstop"; if (gserv_write(buf, 9) != 9) { fprintf(stderr, "stop kgnbd_portd request failed: %s\n", strerror(errno)); return 1; } printm("kgnbd_portd stopped\n"); return 0; } int portstart(unsigned int port) { int len; char buf[16]; if (port != 0) snprintf(buf, 16, "portstart/%u", port); else snprintf(buf, 16, "portstart"); len = strlen(buf); if (gserv_write(buf, len + 1) != len + 1) { fprintf(stderr, "start kgnbd_portd request failed: %s\n", strerror(errno)); return 1; } printm("kgnbd_portd started\n"); return 0; } int list(void) { int n; int bytes = 0; char *serv; char buf[4096]; char *bufptr = buf; while ((n = gserv_read(buf + bytes, 4096 - bytes)) != 0) { if (n < 0 && errno != -EINTR) { fprintf(stderr, "list request failed: %s\n", strerror(errno)); return 1; } bytes += n; } if (bytes < 4096) buf[bytes] = 0; else buf[4095] = 0; bufptr = strchr(buf, '\n'); *bufptr = 0; bufptr = bufptr + 2; printf("%s\n\n", buf); if (strncmp(bufptr, "no", 2) == 0) { serv = strchr(bufptr, '\n'); *serv = 0; printf("%s\n", bufptr); return 0; } while ((serv = strstr(bufptr, "Server")) != NULL) { bufptr = strchr(serv, '/'); *bufptr++ = ' '; bufptr = strchr(bufptr, '/'); *bufptr++ = ' '; bufptr = strstr(serv, "\n pid"); *bufptr++ = 0; printf("%s\n\n", serv); } return 0; } int usage(void) { printf("Usage:\n\n"); printf("gserv [-q] \n"); printf (" Create and export new GNBD from named device or file.\n\n"); printf("gserv [-q] -r \n"); printf(" Remove (un-export) named GNBD.\n\n"); printf("gserv -l\n"); printf(" List exported GNBD's and kgnbd_portd server.\n\n"); printf("gserv [-q] -p []\n"); printf(" Start the kgnbd_portd server (default 14243).\n\n"); printf("gserv [-q] -s\n"); printf(" Stop the current kgnbd_portd server.\n\n"); printf("gserv -h\n"); printf(" Print help.\n\n"); printf("-q toggles gserv to quiet mode, so it does not print on " "success\n\n"); #if 0 printf("Create an new kgnbd server for a file or block device\n" " gserv \n"); printf("Remove a kgnbd server\n" " gserv -r \n"); printf("List the kgnbd and kgnbd_portd servers currently running\n" " gserv -l\n"); printf ("Start up the kgnbd_portd server (if no port is specified, the default is 14243)\n" " gserv -p []\n"); printf("Stop the current kgnbd_portd server\n" " gserv -s\n"); printf("Print this message\n" " gserv -h\n\n"); #endif return 1; } int main(int argc, char **argv) { int c; int i; char *tailptr; unsigned long int port = 0; if (argc < 2) { return usage(); } loop: c = getopt(argc, argv, "qlrpsh"); switch (c) { case 'q': message_flag = 0; goto loop; case -1: if (argc - optind != 2) return usage(); if (get_fd(O_WRONLY) < 0) return 1; return servcreate(argv[optind], argv[optind + 1]); case 'l': if (optind != argc) { fprintf(stderr, "unnecessary argument after list request: %s\n\n", argv[optind]); return usage(); } if (get_fd(O_RDONLY) < 0) return 1; return list(); case 'r': if (get_fd(O_RDWR) < 0) return 1; if (optind == argc) return removeall(); for (i = optind; i < argc; i++) if (servremove(argv[i]) != 0) { fprintf(stderr, "couldn't remove %s: %s\n", argv[i], strerror(errno)); return 1; } return 0; case 'p': if (argc != optind) { fprintf(stderr, "unnecessary argument to kgnbd_portd request\n\n"); return usage(); } /* if (argc - optind > 1){ fprintf(stderr, "unnecessary argument after port number\n\n"); return usage(); } */ if (argc == optind) port = 0; else { port = strtoul(argv[optind], &tailptr, 0); if (port 65536) { fprintf(stderr, "invalid port number: %s\n\n", argv[optind]); return usage(); } } if (get_fd(O_WRONLY) < 0) return 1; return portstart((unsigned int) port); case 's': if (optind != argc) { fprintf(stderr, "unnecessary argument to stop kgnbd_portd " "request\n\n"); return usage(); } if (get_fd(O_WRONLY) < 0) return 1; return portstop(); case '?': if (isprint(optopt)) fprintf(stderr, "Unknown option: -%c\n\n", optopt); else fprintf(stderr, "Unknown option\n\n"); return usage(); case 'h': default: return usage(); } }