| /* |
| drbdadm_usage_cnt.c |
| |
| This file is part of DRBD by Philipp Reisner and Lars Ellenberg. |
| |
| Copyright (C) 2006-2008, LINBIT Information Technologies GmbH |
| Copyright (C) 2006-2008, Philipp Reisner <philipp.reisner@linbit.com> |
| Copyright (C) 2006-2008, Lars Ellenberg <lars.ellenberg@linbit.com> |
| |
| drbd is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| drbd is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with drbd; see the file COPYING. If not, write to |
| the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <setjmp.h> |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #include <arpa/inet.h> |
| #include <netinet/in.h> |
| #include <netdb.h> |
| |
| #include "drbdadm.h" |
| #include "drbdtool_common.h" |
| #include "drbd_endian.h" |
| #include "linux/drbd.h" /* only use DRBD_MAGIC from here! */ |
| |
| #define HTTP_PORT 80 |
| #define HTTP_HOST "usage.drbd.org" |
| #define HTTP_ADDR "212.69.161.111" |
| #define NODE_ID_FILE DRBD_LIB_DIR"/node_id" |
| #define GIT_HASH_BYTE 20 |
| #define SRCVERSION_BYTE 12 /* actually 11 and a half. */ |
| #define SRCVERSION_PAD (GIT_HASH_BYTE - SRCVERSION_BYTE) |
| #define SVN_STYLE_OD 16 |
| |
| struct vcs_rel { |
| uint32_t svn_revision; |
| char git_hash[GIT_HASH_BYTE]; |
| struct { |
| unsigned major, minor, sublvl; |
| } version; |
| unsigned version_code; |
| }; |
| |
| struct node_info { |
| uint64_t node_uuid; |
| struct vcs_rel rev; |
| }; |
| |
| struct node_info_od { |
| uint32_t magic; |
| struct node_info ni; |
| } __packed; |
| |
| /* For our purpose (finding the revision) SLURP_SIZE is always enough. |
| */ |
| static char* slurp_proc_drbd() |
| { |
| const int SLURP_SIZE = 4096; |
| char* buffer; |
| int rr, fd; |
| |
| fd = open("/proc/drbd",O_RDONLY); |
| if( fd == -1) return 0; |
| |
| buffer = malloc(SLURP_SIZE); |
| if(!buffer) return 0; |
| |
| rr = read(fd, buffer, SLURP_SIZE-1); |
| if( rr == -1) { |
| free(buffer); |
| return 0; |
| } |
| |
| buffer[rr]=0; |
| close(fd); |
| |
| return buffer; |
| } |
| |
| void read_hex(char* dst, char* src, int dst_size, int src_size) |
| { |
| int dst_i, u, src_i=0; |
| |
| for(dst_i=0;dst_i<dst_size;dst_i++) { |
| if (src[src_i] == 0) break; |
| if (src_size - src_i < 2) { |
| sscanf(src+src_i,"%1x",&u); |
| dst[dst_i]=u<<4; |
| } else { |
| sscanf(src+src_i,"%2x",&u); |
| dst[dst_i]=u; |
| } |
| if(++src_i >= src_size) break; |
| if(src[src_i] == 0) break; |
| if(++src_i >= src_size) break; |
| } |
| } |
| |
| void vcs_ver_from_str(struct vcs_rel *rel, const char *token) |
| { |
| char *dot; |
| long maj, min, sub; |
| maj = strtol(token, &dot, 10); |
| if (*dot != '.') |
| return; |
| min = strtol(dot+1, &dot, 10); |
| if (*dot != '.') |
| return; |
| sub = strtol(dot+1, &dot, 10); |
| /* don't check on *dot == 0, |
| * we may want to add some extraversion tag sometime |
| if (*dot != 0) |
| return; |
| */ |
| |
| rel->version.major = maj; |
| rel->version.minor = min; |
| rel->version.sublvl = sub; |
| |
| rel->version_code = (maj << 16) + (min << 8) + sub; |
| } |
| |
| void vcs_from_str(struct vcs_rel *rel, const char *text) |
| { |
| char token[80]; |
| int plus=0; |
| enum { begin, f_ver, f_svn, f_rev, f_git, f_srcv } ex = begin; |
| |
| while (sget_token(token, sizeof(token), &text) != EOF) { |
| switch(ex) { |
| case begin: |
| if(!strcmp(token,"version:")) |
| ex = f_ver; |
| if(!strcmp(token,"SVN")) ex = f_svn; |
| if(!strcmp(token,"GIT-hash:")) ex = f_git; |
| if(!strcmp(token,"srcversion:")) ex = f_srcv; |
| break; |
| case f_ver: |
| if(!strcmp(token,"plus")) |
| plus = 1; |
| /* still waiting for version */ |
| else { |
| vcs_ver_from_str(rel, token); |
| ex = begin; |
| } |
| break; |
| case f_svn: |
| if(!strcmp(token,"Revision:")) ex = f_rev; |
| break; |
| case f_rev: |
| rel->svn_revision = atol(token) * 10; |
| if( plus ) rel->svn_revision += 1; |
| memset(rel->git_hash, 0, GIT_HASH_BYTE); |
| return; |
| case f_git: |
| read_hex(rel->git_hash, token, GIT_HASH_BYTE, strlen(token)); |
| rel->svn_revision = 0; |
| return; |
| case f_srcv: |
| memset(rel->git_hash, 0, SRCVERSION_PAD); |
| read_hex(rel->git_hash + SRCVERSION_PAD, token, SRCVERSION_BYTE, strlen(token)); |
| rel->svn_revision = 0; |
| return; |
| } |
| } |
| } |
| |
| static int current_vcs_is_from_proc_drbd; |
| static struct vcs_rel current_vcs_rel; |
| static struct vcs_rel userland_version; |
| static void vcs_get_current(void) |
| { |
| char* version_txt; |
| |
| if (current_vcs_rel.version_code) |
| return; |
| |
| version_txt = slurp_proc_drbd(); |
| if(version_txt) { |
| vcs_from_str(¤t_vcs_rel, version_txt); |
| current_vcs_is_from_proc_drbd = 1; |
| free(version_txt); |
| } else { |
| vcs_from_str(¤t_vcs_rel, drbd_buildtag()); |
| vcs_ver_from_str(¤t_vcs_rel, PACKAGE_VERSION); |
| } |
| } |
| |
| static void vcs_get_userland(void) |
| { |
| if (userland_version.version_code) |
| return; |
| vcs_ver_from_str(&userland_version, PACKAGE_VERSION); |
| } |
| |
| int version_code_kernel(void) |
| { |
| vcs_get_current(); |
| return current_vcs_is_from_proc_drbd |
| ? current_vcs_rel.version_code |
| : 0; |
| } |
| |
| int version_code_userland(void) |
| { |
| vcs_get_userland(); |
| return userland_version.version_code; |
| } |
| |
| static int vcs_eq(struct vcs_rel *rev1, struct vcs_rel *rev2) |
| { |
| if( rev1->svn_revision || rev2->svn_revision ) { |
| return rev1->svn_revision == rev2->svn_revision; |
| } else { |
| return !memcmp(rev1->git_hash,rev2->git_hash,GIT_HASH_BYTE); |
| } |
| } |
| |
| static int vcs_ver_cmp(struct vcs_rel *rev1, struct vcs_rel *rev2) |
| { |
| return rev1->version_code - rev2->version_code; |
| } |
| |
| void warn_on_version_mismatch(void) |
| { |
| char *msg; |
| int cmp; |
| |
| /* get the kernel module version from /proc/drbd */ |
| vcs_get_current(); |
| |
| /* get the userland version from PACKAGE_VERSION */ |
| vcs_get_userland(); |
| |
| cmp = vcs_ver_cmp(&userland_version, ¤t_vcs_rel); |
| /* no message if equal */ |
| if (cmp == 0) |
| return; |
| if (cmp > 0xffff || cmp < -0xffff) /* major version differs! */ |
| msg = "mixing different major numbers will not work!"; |
| else if (cmp < 0) /* userland is older. always warn. */ |
| msg = "you should upgrade your drbd tools!"; |
| else if (cmp & 0xff00) /* userland is newer minor version */ |
| msg = "please don't mix different DRBD series."; |
| else /* userland is newer, but only differ in sublevel. */ |
| msg = "preferably kernel and userland versions should match."; |
| |
| fprintf(stderr, "DRBD module version: %u.%u.%u\n" |
| " userland version: %u.%u.%u\n%s\n", |
| current_vcs_rel.version.major, |
| current_vcs_rel.version.minor, |
| current_vcs_rel.version.sublvl, |
| userland_version.version.major, |
| userland_version.version.minor, |
| userland_version.version.sublvl, |
| msg); |
| } |
| |
| void add_lib_drbd_to_path(void) |
| { |
| char *new_path = NULL; |
| char *old_path = getenv("PATH"); |
| |
| m_asprintf(&new_path, "%s%s%s", |
| old_path, |
| old_path ? ":" : "", |
| "/lib/drbd"); |
| setenv("PATH", new_path, 1); |
| } |
| |
| void maybe_exec_drbdadm_83(char **argv) |
| { |
| if (current_vcs_rel.version.major == 8 && |
| current_vcs_rel.version.minor == 3) { |
| #ifdef DRBD_LEGACY_83 |
| /* This drbdadm warned already... */ |
| setenv("DRBD_DONT_WARN_ON_VERSION_MISMATCH", "1", 0); |
| add_lib_drbd_to_path(); |
| execvp(drbdadm_83, argv); |
| fprintf(stderr, "execvp() failed to exec %s: %m\n", drbdadm_83); |
| #else |
| fprintf(stderr, "This drbdadm was not built with support for drbd-8.3\n" |
| "Consider to rebuild with ./configure --with-83-support\n"); |
| #endif |
| exit(E_EXEC_ERROR); |
| } |
| } |
| |
| static char *vcs_to_str(struct vcs_rel *rev) |
| { |
| static char buffer[80]; // Not generic, sufficient for the purpose. |
| |
| if( rev->svn_revision ) { |
| snprintf(buffer,80,"nv="U32,rev->svn_revision); |
| } else { |
| int len=20,p; |
| unsigned char *bytes; |
| |
| p = sprintf(buffer,"git="); |
| bytes = (unsigned char*)rev->git_hash; |
| while(len--) p += sprintf(buffer+p,"%02x",*bytes++); |
| } |
| return buffer; |
| } |
| |
| static void write_node_id(struct node_info *ni) |
| { |
| int fd; |
| struct node_info_od on_disk; |
| int size; |
| |
| fd = open(NODE_ID_FILE,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR); |
| if( fd == -1 && errno == ENOENT) { |
| mkdir(DRBD_LIB_DIR,S_IRWXU); |
| fd = open(NODE_ID_FILE,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR); |
| } |
| |
| if( fd == -1) { |
| perror("Creation of "NODE_ID_FILE" failed."); |
| exit(20); |
| } |
| |
| if(ni->rev.svn_revision != 0) { // SVN style (old) |
| on_disk.magic = cpu_to_be32(DRBD_MAGIC); |
| on_disk.ni.node_uuid = cpu_to_be64(ni->node_uuid); |
| on_disk.ni.rev.svn_revision = cpu_to_be32(ni->rev.svn_revision); |
| memset(on_disk.ni.rev.git_hash,0,GIT_HASH_BYTE); |
| size = SVN_STYLE_OD; |
| } else { |
| on_disk.magic = cpu_to_be32(DRBD_MAGIC+1); |
| on_disk.ni.node_uuid = cpu_to_be64(ni->node_uuid); |
| on_disk.ni.rev.svn_revision = 0; |
| memcpy(on_disk.ni.rev.git_hash,ni->rev.git_hash,GIT_HASH_BYTE); |
| size = sizeof(on_disk); |
| } |
| |
| if( write(fd,&on_disk, size) != size) { |
| perror("Write to "NODE_ID_FILE" failed."); |
| exit(20); |
| } |
| |
| close(fd); |
| } |
| |
| |
| static int read_node_id(struct node_info *ni) |
| { |
| int rr; |
| int fd; |
| struct node_info_od on_disk; |
| |
| fd = open(NODE_ID_FILE, O_RDONLY); |
| if (fd == -1) { |
| return 0; |
| } |
| |
| rr = read(fd, &on_disk, sizeof(on_disk)); |
| if (rr != sizeof(on_disk) && rr != SVN_STYLE_OD) { |
| close(fd); |
| return 0; |
| } |
| |
| switch (be32_to_cpu(on_disk.magic)) { |
| case DRBD_MAGIC: |
| ni->node_uuid = be64_to_cpu(on_disk.ni.node_uuid); |
| ni->rev.svn_revision = be32_to_cpu(on_disk.ni.rev.svn_revision); |
| memset(ni->rev.git_hash, 0, GIT_HASH_BYTE); |
| break; |
| case DRBD_MAGIC+1: |
| ni->node_uuid = be64_to_cpu(on_disk.ni.node_uuid); |
| ni->rev.svn_revision = 0; |
| memcpy(ni->rev.git_hash, on_disk.ni.rev.git_hash, GIT_HASH_BYTE); |
| break; |
| default: |
| return 0; |
| } |
| |
| close(fd); |
| return 1; |
| } |
| |
| /* What we probably should do is use getaddrinfo_a(), |
| * instead of alarm() and siglongjump limited gethostbyname(), |
| * but I don't like implicit threads. */ |
| /* to interrupt gethostbyname, |
| * we not only need a signal, |
| * but also the long jump: |
| * gethostbyname would otherwise just restart the syscall |
| * and timeout again. */ |
| static sigjmp_buf timed_out; |
| static void gethostbyname_timeout(int __attribute((unused)) signo) |
| { |
| siglongjmp(timed_out, 1); |
| } |
| |
| #define DNS_TIMEOUT 3 /* seconds */ |
| #define SOCKET_TIMEOUT 3 /* seconds */ |
| struct hostent *my_gethostbyname(const char *name) |
| { |
| struct sigaction sa; |
| struct sigaction so; |
| struct hostent *h; |
| static int failed_once_already = 0; |
| |
| if (failed_once_already) |
| return NULL; |
| |
| alarm(0); |
| sa.sa_handler = &gethostbyname_timeout; |
| sigemptyset(&sa.sa_mask); |
| sa.sa_flags = SA_NODEFER; |
| |
| sigaction(SIGALRM, &sa, &so); |
| |
| if (!sigsetjmp(timed_out, 1)) { |
| struct hostent ret; |
| char buf[2048]; |
| int my_h_errno; |
| |
| alarm(DNS_TIMEOUT); |
| /* h = gethostbyname(name); |
| * If the resolver is unresponsive, |
| * we may siglongjmp out of a "critical section" of gethostbyname, |
| * still holding some glibc internal lock. |
| * Any later attempt to call gethostbyname() would then deadlock |
| * (last syscall would be futex(...)) |
| * |
| * gethostbyname_r() apparently does not use any internal locks. |
| * Even if unnecessary in our case, it feels less dirty. |
| */ |
| gethostbyname_r(name, &ret, buf, sizeof(buf), &h, &my_h_errno); |
| } else { |
| /* timed out, longjmp of SIGALRM jumped here */ |
| h = NULL; |
| } |
| |
| alarm(0); |
| sigaction(SIGALRM, &so, NULL); |
| |
| if (h == NULL) |
| failed_once_already = 1; |
| return h; |
| } |
| |
| /** |
| * insert_usage_with_socket: |
| * |
| * Return codes: |
| * |
| * 0 - success |
| * 1 - failed to create socket |
| * 2 - unknown server |
| * 3 - cannot connect to server |
| * 5 - other error |
| */ |
| static int make_get_request(char *uri) { |
| struct sockaddr_in server; |
| struct hostent *host_info; |
| unsigned long addr; |
| int sock; |
| char *req_buf; |
| char *http_host = HTTP_HOST; |
| int buf_len = 1024; |
| char buffer[buf_len]; |
| FILE *sockfd; |
| int writeit; |
| struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT }; |
| |
| sock = socket( PF_INET, SOCK_STREAM, 0); |
| if (sock < 0) |
| return 1; |
| |
| setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); |
| setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); |
| |
| memset (&server, 0, sizeof(server)); |
| |
| /* convert host name to ip */ |
| host_info = my_gethostbyname(http_host); |
| if (host_info == NULL) { |
| /* unknown host, try with ip */ |
| if ((addr = inet_addr( HTTP_ADDR )) != INADDR_NONE) |
| memcpy((char *)&server.sin_addr, &addr, sizeof(addr)); |
| else { |
| close(sock); |
| return 2; |
| } |
| } else { |
| memcpy((char *)&server.sin_addr, host_info->h_addr, |
| host_info->h_length); |
| } |
| |
| |
| ssprintf(req_buf, |
| "GET %s HTTP/1.0\r\n" |
| "Host: "HTTP_HOST"\r\n" |
| "User-Agent: drbdadm/"PACKAGE_VERSION" (%s; %s; %s; %s)\r\n" |
| "\r\n", |
| uri, |
| nodeinfo.sysname, nodeinfo.release, |
| nodeinfo.version, nodeinfo.machine); |
| |
| server.sin_family = AF_INET; |
| server.sin_port = htons(HTTP_PORT); |
| |
| if (connect(sock, (struct sockaddr*)&server, sizeof(server))<0) { |
| /* cannot connect to server */ |
| close(sock); |
| return 3; |
| } |
| |
| if ((sockfd = fdopen(sock, "r+")) == NULL) { |
| close(sock); |
| return 5; |
| } |
| |
| if (fputs(req_buf, sockfd) == EOF) { |
| fclose(sockfd); |
| close(sock); |
| return 5; |
| } |
| |
| writeit = 0; |
| while (fgets(buffer, buf_len, sockfd) != NULL) { |
| /* ignore http headers */ |
| if (writeit == 0) { |
| if (buffer[0] == '\r' || buffer[0] == '\n') |
| writeit = 1; |
| } else { |
| fprintf(stderr,"%s", buffer); |
| } |
| } |
| fclose(sockfd); |
| close(sock); |
| return 0; |
| } |
| |
| static void url_encode(char *in, char *out) |
| { |
| char *h = "0123456789abcdef"; |
| unsigned char c; |
| |
| while ((c = *in++) != 0) { |
| if (c == '\n') |
| break; |
| if (('a' <= c && c <= 'z') || |
| ('A' <= c && c <= 'Z') || |
| ('0' <= c && c <= '9') || |
| c == '-' || c == '_' || c == '.') |
| *out++ = c; |
| else if (c == ' ') |
| *out++ = '+'; |
| else { |
| *out++ = '%'; |
| *out++ = h[c >> 4]; |
| *out++ = h[c & 0x0f]; |
| } |
| } |
| *out = 0; |
| } |
| |
| /* Ensure that the node is counted on http://usage.drbd.org |
| */ |
| #define ANSWER_SIZE 80 |
| |
| void uc_node(enum usage_count_type type) |
| { |
| struct node_info ni; |
| char *uri; |
| int send = 0; |
| int update = 0; |
| char answer[ANSWER_SIZE]; |
| char n_comment[ANSWER_SIZE*3]; |
| char *r; |
| |
| if( type == UC_NO ) return; |
| if( getuid() != 0 ) return; |
| |
| /* not when running directly from init, |
| * or if stdout is no tty. |
| * you do not want to have the "user information message" |
| * as output from `drbdadm sh-resources all` |
| */ |
| if (getenv("INIT_VERSION")) return; |
| if (no_tty) return; |
| |
| vcs_get_current(); |
| |
| if( ! read_node_id(&ni) ) { |
| get_random_bytes(&ni.node_uuid,sizeof(ni.node_uuid)); |
| ni.rev = current_vcs_rel; |
| send = 1; |
| } else if (current_vcs_is_from_proc_drbd == 0) { |
| /* Avoid flapping between drbd-utils git-hash and |
| * kernel module git-hash. */ |
| send = 0; |
| } else { |
| // read_node_id() was successful |
| if (!vcs_eq(&ni.rev,¤t_vcs_rel)) { |
| ni.rev = current_vcs_rel; |
| update = 1; |
| send = 1; |
| } |
| } |
| |
| if(!send) return; |
| |
| n_comment[0]=0; |
| if (type == UC_ASK ) { |
| fprintf(stderr, |
| "\n" |
| "\t\t--== This is %s of DRBD ==--\n" |
| "Please take part in the global DRBD usage count at http://"HTTP_HOST".\n\n" |
| "The counter works anonymously. It creates a random number to identify\n" |
| "your machine and sends that random number, along with the kernel and\n" |
| "DRBD version, to "HTTP_HOST".\n\n" |
| "The benefits for you are:\n" |
| " * In response to your submission, the server ("HTTP_HOST") will tell you\n" |
| " how many users before you have installed this version (%s).\n" |
| " * With a high counter LINBIT has a strong motivation to\n" |
| " continue funding DRBD's development.\n\n" |
| "http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&%s\n\n" |
| "In case you want to participate but know that this machine is firewalled,\n" |
| "simply issue the query string with your favorite web browser or wget.\n" |
| "You can control all of this by setting 'usage-count' in your drbd.conf.\n\n" |
| "* You may enter a free form comment about your machine, that gets\n" |
| " used on "HTTP_HOST" instead of the big random number.\n" |
| "* If you wish to opt out entirely, simply enter 'no'.\n" |
| "* To count this node without comment, just press [RETURN]\n", |
| update ? "an update" : "a new installation", |
| PACKAGE_VERSION,ni.node_uuid, vcs_to_str(&ni.rev)); |
| r = fgets(answer, ANSWER_SIZE, stdin); |
| if(r && !strcmp(answer,"no\n")) send = 0; |
| url_encode(answer,n_comment); |
| } |
| |
| ssprintf(uri,"http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&%s%s%s", |
| ni.node_uuid, vcs_to_str(&ni.rev), |
| n_comment[0] ? "&nc=" : "", n_comment); |
| |
| if (send) { |
| write_node_id(&ni); |
| |
| fprintf(stderr, |
| "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" |
| " --== Thank you for participating in the global usage survey ==--\n" |
| "The server's response is:\n\n"); |
| make_get_request(uri); |
| if (type == UC_ASK) { |
| fprintf(stderr, |
| "\n" |
| "From now on, drbdadm will contact "HTTP_HOST" only when you update\n" |
| "DRBD or when you use 'drbdadm create-md'. Of course it will continue\n" |
| "to ask you for confirmation as long as 'usage-count' is at its default\n" |
| "value of 'ask'.\n\n" |
| "Just press [RETURN] to continue: "); |
| r = fgets(answer, 9, stdin); |
| } |
| } |
| } |
| |
| /* For our purpose (finding the revision) SLURP_SIZE is always enough. |
| */ |
| static char* run_admm_generic(struct cfg_ctx *ctx, const char *arg_override) |
| { |
| const int SLURP_SIZE = 4096; |
| int rr,pipes[2]; |
| char* buffer; |
| pid_t pid; |
| |
| buffer = malloc(SLURP_SIZE); |
| if(!buffer) return 0; |
| |
| if(pipe(pipes)) return 0; |
| |
| pid = fork(); |
| if(pid == -1) { |
| fprintf(stderr,"Can not fork\n"); |
| exit(E_EXEC_ERROR); |
| } |
| if(pid == 0) { |
| // child |
| close(pipes[0]); // close reading end |
| dup2(pipes[1],1); // 1 = stdout |
| close(pipes[1]); |
| /* local modification in child, |
| * no propagation to parent */ |
| ctx->arg = arg_override; |
| rr = _admm_generic(ctx, |
| SLEEPS_VERY_LONG| |
| DONT_REPORT_FAILED); |
| exit(rr); |
| } |
| close(pipes[1]); // close writing end |
| |
| rr = read(pipes[0], buffer, SLURP_SIZE-1); |
| if( rr == -1) { |
| free(buffer); |
| // FIXME cleanup |
| return 0; |
| } |
| buffer[rr]=0; |
| close(pipes[0]); |
| |
| waitpid(pid,0,0); |
| |
| return buffer; |
| } |
| |
| int adm_create_md(struct cfg_ctx *ctx) |
| { |
| char answer[ANSWER_SIZE]; |
| struct node_info ni; |
| uint64_t device_uuid=0; |
| uint64_t device_size=0; |
| char *uri; |
| int send=0; |
| char *tb; |
| int rv, fd, verbose_tmp; |
| char *r; |
| |
| verbose_tmp = verbose; |
| verbose = 0; |
| tb = run_admm_generic(ctx, "read-dev-uuid"); |
| verbose = verbose_tmp; |
| device_uuid = strto_u64(tb,NULL,16); |
| free(tb); |
| |
| /* this is "drbdmeta ... create-md" */ |
| rv = _admm_generic(ctx, SLEEPS_VERY_LONG); |
| |
| if(rv || dry_run) return rv; |
| |
| fd = open(ctx->vol->disk,O_RDONLY); |
| if( fd != -1) { |
| device_size = bdev_size(fd); |
| close(fd); |
| } |
| |
| if( read_node_id(&ni) && device_size && !device_uuid) { |
| get_random_bytes(&device_uuid, sizeof(uint64_t)); |
| |
| if( global_options.usage_count == UC_YES ) send = 1; |
| if( global_options.usage_count == UC_ASK ) { |
| fprintf(stderr, |
| "\n" |
| "\t\t--== Creating metadata ==--\n" |
| "As with nodes, we count the total number of devices mirrored by DRBD\n" |
| "at http://"HTTP_HOST".\n\n" |
| "The counter works anonymously. It creates a random number to identify\n" |
| "the device and sends that random number, along with the kernel and\n" |
| "DRBD version, to "HTTP_HOST".\n\n" |
| "http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&ru="U64"&rs="U64"\n\n" |
| "* If you wish to opt out entirely, simply enter 'no'.\n" |
| "* To continue, just press [RETURN]\n", |
| ni.node_uuid,device_uuid,device_size |
| ); |
| r = fgets(answer, ANSWER_SIZE, stdin); |
| if(r && strcmp(answer,"no\n")) send = 1; |
| } |
| } |
| |
| if(!device_uuid) { |
| get_random_bytes(&device_uuid, sizeof(uint64_t)); |
| } |
| |
| if (send) { |
| ssprintf(uri,"http://"HTTP_HOST"/cgi-bin/insert_usage.pl?" |
| "nu="U64"&ru="U64"&rs="U64, |
| ni.node_uuid, device_uuid, device_size); |
| make_get_request(uri); |
| } |
| |
| /* HACK */ |
| { |
| struct cfg_ctx local_ctx = *ctx; |
| struct setup_option *old_setup_options; |
| char *opt; |
| |
| ssprintf(opt, X64(016), device_uuid); |
| old_setup_options = setup_options; |
| setup_options = NULL; |
| add_setup_option(false, opt); |
| |
| local_ctx.arg = "write-dev-uuid"; |
| _admm_generic(&local_ctx, SLEEPS_VERY_LONG); |
| |
| free(setup_options); |
| setup_options = old_setup_options; |
| } |
| return rv; |
| } |
| |