/* * Copyright (c) 2014, Red Hat * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. Redistributions in binary * form must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials * provided with the distribution. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "mem-pool.h" /* * Mock implementation of etcd * The etcd file is simulated in /tmp/ * Writes from Multiple writers are protected using file lock. */ #include "etcd-api.h" #define MAX_KEY_LEN 64 #define MAX_VALUE_LEN 64 #define MAX_EXPIRE_LEN 16 etcd_session etcd_open (etcd_server *server_list) { return NULL; } typedef struct _etcd_sim_s { char *path; } etcd_sim_t; void etcd_close (etcd_session this) { etcd_sim_t *sim = (etcd_sim_t *)this; free(sim->path); free(this); } char * etcd_get_1 (FILE *stream, char *key) { char *str = NULL; size_t len; unsigned long expires; char *ret; // Read the file while(1) { if(str) { free(str); str = NULL; } if (getline((char **)&str, &len,stream) == -1) { break; } if (!strncmp(str, key, strlen(key))) { char k[256], s[256]; sscanf(str,"%s %s %lu",k, s, &expires); // check if key is expired. if (time(NULL) > expires) { /* Keep looking for an unexpired entry. */ continue; } ret = calloc(1, strlen(s) + 1); strcpy(ret,s); free(str); return(ret); } } return NULL; } char * etcd_get (etcd_session this, char *key) { etcd_sim_t *sim = (etcd_sim_t *)this; int fd; FILE *stream; char *retval; fd = open(sim->path,O_RDONLY); if (!fd) { return NULL; } stream = fdopen(fd,"r"); (void)flock(fd,LOCK_SH); retval = etcd_get_1(stream,key); (void)flock(fd,LOCK_UN); fclose(stream); /* closes fd as well */ return retval; } etcd_result etcd_set_1 (FILE *stream, char *key, char *value, char *precond, unsigned int ttl) { char *str = NULL; char tp[255]; size_t len; unsigned long expires; while(1) { if(str) { free(str); str = NULL; } if (getline((char **)&str, &len,stream) == -1) { break; } if (!strncmp(str, key, strlen(key))) { char k[256], s[256]; sscanf(str,"%s %s %lu",k, s, &expires); // check if the present key is expired if (time(NULL) > expires) { /* Keep looking for an unexpired entry. */ continue; } /* * The only case in which we should fail here is if a * precondition was specified and does not match the * current (non-expired) value. */ if (precond && strcmp(precond, s)) { free(str); return ETCD_WTF; } fseek(stream, -strlen(str), SEEK_CUR); free(str); goto here; } } here: memset(tp, 0, 255); sprintf(tp,"%*s %*s %*lu\n", -MAX_KEY_LEN, key, -MAX_VALUE_LEN, value, -MAX_EXPIRE_LEN, ttl ? time(NULL) + ttl : ~0); if (fwrite(tp, 1,strlen(tp), stream) != strlen(tp)) { return ETCD_WTF; } fflush(stream); fsync(fileno(stream)); return ETCD_OK; } etcd_result etcd_set (etcd_session this, char *key, char *value, char *precond, unsigned int ttl) { etcd_sim_t *sim = (etcd_sim_t *)this; int fd; FILE *stream; etcd_result retval; fd = open(sim->path,O_RDWR); if (fd < 0) { return ETCD_WTF; } stream = fdopen(fd,"r+"); (void)flock(fd,LOCK_EX); retval = etcd_set_1(stream,key,value,precond,ttl); (void)flock(fd,LOCK_UN); fclose(stream); /* closes fd as well */ return retval; } etcd_session etcd_open_str (char *server_names) { etcd_sim_t *sim; int fd; sim = calloc(1, sizeof(etcd_sim_t)); (void)asprintf(&sim->path,"/tmp/%s",server_names); fd = open(sim->path, O_RDWR | O_CREAT, 0777); if (fd == -1) { free(sim->path); free(sim); return NULL; } close(fd); return ((void *)sim); } void etcd_close_str (etcd_session this) { etcd_close(this); } etcd_result etcd_delete (etcd_session this, char *key) { return ETCD_WTF; } char * etcd_leader (etcd_session this_as_void) { return NULL; } etcd_result etcd_watch (etcd_session this, char *pfx, char **keyp, char **valuep, int *index_in, int *index_out) { return ETCD_WTF; } etcd_result etcd_lock (etcd_session session_as_void, char *key, unsigned int ttl, char *index_in, char **index_out) { char *path; int fd; if (!index_in) { if (gf_asprintf(&path,"/var/tmp/%s",key) < 0) { return ETCD_WTF; } fd = open(path,O_RDWR|O_CREAT,0666); GF_FREE(path); if (fd < 0) { return ETCD_WTF; } if (flock(fd,LOCK_EX) < 0) { close(fd); return ETCD_WTF; } *index_out = strdup("42"); } /* * Yes, we leak an fd by not closing it here (and nobody else even * knows about it). That would be awful in any other context, but * for test scripts it won't matter. */ return ETCD_OK; }