client.c (5109B)
1 #define _GNU_SOURCE 2 #include <errno.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <sys/socket.h> 7 #include <sys/time.h> 8 #include <sys/un.h> 9 #include <unistd.h> 10 11 #include "finwo/resp.h" 12 13 #include "cli/client.h" 14 #include "config/daemon.h" 15 16 #define CLIENT_TIMEOUT_S 10 17 18 static int connect_daemon(const char **path_out) { 19 const char *sockpath = linkd_client_socket(); 20 *path_out = sockpath; 21 22 int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 23 if (fd < 0) { 24 perror("socket"); 25 return -1; 26 } 27 28 struct sockaddr_un addr; 29 memset(&addr, 0, sizeof(addr)); 30 addr.sun_family = AF_UNIX; 31 strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path) - 1); 32 33 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 34 fprintf(stderr, "connect %s failed: %s\nIs linkd running?\n", 35 sockpath, strerror(errno)); 36 close(fd); 37 return -1; 38 } 39 40 struct timeval tv = { .tv_sec = CLIENT_TIMEOUT_S, .tv_usec = 0 }; 41 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); 42 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); 43 return fd; 44 } 45 46 static const char *map_get(const resp_object *m, const char *key) { 47 if (!m || m->type != RESPT_ARRAY) return NULL; 48 for (size_t i = 0; i + 1 < m->u.arr.n; i += 2) { 49 const resp_object *k = &m->u.arr.elem[i]; 50 if ((k->type == RESPT_BULK || k->type == RESPT_SIMPLE) && k->u.s && 51 !strcmp(k->u.s, key)) { 52 const resp_object *v = &m->u.arr.elem[i + 1]; 53 if (v->type == RESPT_BULK || v->type == RESPT_SIMPLE) return v->u.s; 54 } 55 } 56 return NULL; 57 } 58 59 // An interface map renders as the config syntax it came from, so ifquery 60 // output can be pasted back into /etc/network/interfaces. 61 static int render_iface_map(const resp_object *m) { 62 const char *name = map_get(m, "name"); 63 if (!name) return 0; 64 65 printf("iface %s\n", name); 66 for (size_t i = 0; i + 1 < m->u.arr.n; i += 2) { 67 const resp_object *k = &m->u.arr.elem[i]; 68 const resp_object *v = &m->u.arr.elem[i + 1]; 69 if (k->type != RESPT_BULK && k->type != RESPT_SIMPLE) continue; 70 if (!strcmp(k->u.s, "name")) continue; 71 if (v->type == RESPT_BULK || v->type == RESPT_SIMPLE) { 72 printf(" %s %s\n", k->u.s, v->u.s); 73 } else if (v->type == RESPT_INT) { 74 printf(" %s %lld\n", k->u.s, v->u.i); 75 } 76 } 77 return 1; 78 } 79 80 static void render(const resp_object *o, int depth) { 81 if (!o) return; 82 switch (o->type) { 83 case RESPT_SIMPLE: 84 printf("%s\n", o->u.s ? o->u.s : ""); 85 break; 86 case RESPT_BULK: 87 if (o->u.s) fputs(o->u.s, stdout); 88 if (o->u.s && o->u.s[strlen(o->u.s) - 1] != '\n') fputc('\n', stdout); 89 break; 90 case RESPT_INT: 91 printf("%lld\n", o->u.i); 92 break; 93 case RESPT_ARRAY: 94 if (depth == 0 && render_iface_map(o)) break; 95 for (size_t i = 0; i < o->u.arr.n; i++) { 96 const resp_object *e = &o->u.arr.elem[i]; 97 if (e->type == RESPT_ARRAY && render_iface_map(e)) continue; 98 render(e, depth + 1); 99 } 100 break; 101 default: 102 break; 103 } 104 } 105 106 static int send_obj(int fd, resp_object *o) { 107 char *buf = NULL; 108 size_t len = 0; 109 int rc = resp_serialize(o, &buf, &len); 110 resp_free(o); 111 if (rc != 0) return -1; 112 113 size_t sent = 0; 114 while (sent < len) { 115 ssize_t n = write(fd, buf + sent, len - sent); 116 if (n <= 0) { 117 if (n < 0 && errno == EINTR) continue; 118 fprintf(stderr, "write failed: %s\n", strerror(errno)); 119 free(buf); 120 return -1; 121 } 122 sent += (size_t)n; 123 } 124 free(buf); 125 return 0; 126 } 127 128 // $LINKD_AUTH as "user:password", or just "password" for the default user. 129 static int maybe_auth(int fd) { 130 const char *cred = getenv("LINKD_AUTH"); 131 if (!cred || !*cred) return 0; 132 133 char *copy = strdup(cred); 134 if (!copy) return -1; 135 char *sep = strchr(copy, ':'); 136 const char *user = "default"; 137 const char *pass = copy; 138 if (sep) { *sep = '\0'; user = copy; pass = sep + 1; } 139 140 resp_object *a = resp_array_init(); 141 resp_array_append_bulk(a, "AUTH"); 142 resp_array_append_bulk(a, user); 143 resp_array_append_bulk(a, pass); 144 free(copy); 145 146 if (send_obj(fd, a) != 0) return -1; 147 148 resp_object *r = resp_read(fd); 149 if (!r) { 150 fprintf(stderr, "no reply to AUTH\n"); 151 return -1; 152 } 153 int bad = (r->type == RESPT_ERROR); 154 if (bad) fprintf(stderr, "%s\n", r->u.s ? r->u.s : "AUTH failed"); 155 resp_free(r); 156 return bad ? -1 : 0; 157 } 158 159 int linkd_call(int argc, const char **argv) { 160 const char *sockpath = NULL; 161 int fd = connect_daemon(&sockpath); 162 if (fd < 0) return 1; 163 164 if (maybe_auth(fd) != 0) { close(fd); return 1; } 165 166 resp_object *req = resp_array_init(); 167 if (!req) { close(fd); return 1; } 168 for (int i = 0; i < argc; i++) resp_array_append_bulk(req, argv[i]); 169 170 if (send_obj(fd, req) != 0) { close(fd); return 1; } 171 172 resp_object *reply = resp_read(fd); 173 close(fd); 174 175 if (!reply) { 176 fprintf(stderr, "no reply from linkd\n"); 177 return 1; 178 } 179 180 int status = 0; 181 if (reply->type == RESPT_ERROR) { 182 fprintf(stderr, "%s\n", reply->u.s ? reply->u.s : "error"); 183 status = 1; 184 } else { 185 render(reply, 0); 186 } 187 resp_free(reply); 188 return status; 189 }