1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <json_object.h>
#include "vali.h"
#include "conn.h"
struct vali_client_call {
struct vali_client *client;
};
struct vali_client {
struct vali_conn conn;
struct vali_client_call *pending_call;
};
struct vali_client *vali_client_connect_fd(int fd) {
struct vali_client *client = calloc(1, sizeof(*client));
if (client == NULL) {
return NULL;
}
conn_init(&client->conn, fd);
return client;
}
struct vali_client *vali_client_connect_unix(const char *path) {
struct sockaddr_un addr;
if (!set_sockaddr_un(&addr, path)) {
errno = -EINVAL;
return false;
}
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
return NULL;
}
if (!set_cloexec(fd)) {
goto err_fd;
}
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
goto err_fd;
}
struct vali_client *client = vali_client_connect_fd(fd);
if (client == NULL) {
goto err_fd;
}
return client;
err_fd:
close(fd);
return NULL;
}
void vali_client_destroy(struct vali_client *client) {
conn_finish(&client->conn);
free(client);
}
static bool client_send(struct vali_client *client, struct json_object *req) {
return conn_enqueue(&client->conn, req) && conn_flush(&client->conn);
}
static bool client_receive(struct vali_client *client,
struct json_object **out, struct vali_error *err, bool *continues) {
if (out != NULL) {
*out = NULL;
}
if (err != NULL) {
*err = (struct vali_error){0};
}
bool eof = false;
struct json_object *resp = NULL;
if (!conn_receive(&client->conn, &eof) || !conn_dequeue(&client->conn, &resp) || resp == NULL) {
return false;
}
struct json_object *params = json_object_object_get(resp, "parameters");
const char *error_name = json_object_get_string(json_object_object_get(resp, "error"));
*continues = json_object_get_boolean(json_object_object_get(resp, "continues"));
bool ok = error_name == NULL;
if (out != NULL && ok) {
*out = json_object_get(params);
}
if (err != NULL && !ok) {
vali_error_set(err, error_name, params);
}
json_object_put(resp);
return ok;
}
bool vali_client_call(struct vali_client *client,
const char *method, struct json_object *in, struct json_object **out,
struct vali_error *err) {
assert(in == NULL || json_object_get_type(in) == json_type_object);
struct json_object *req = json_object_new_object();
struct json_object *method_obj = json_object_new_string(method);
if (req == NULL || method_obj == NULL) {
return false;
}
json_object_object_add(req, "method", method_obj);
json_object_object_add(req, "parameters", in);
bool continues = false;
return client_send(client, req) && client_receive(client, out, err, &continues) && !continues;
}
struct vali_client_call *vali_client_call_more(struct vali_client *client,
const char *method, struct json_object *in) {
assert(in == NULL || json_object_get_type(in) == json_type_object);
struct json_object *req = json_object_new_object();
struct json_object *method_obj = json_object_new_string(method);
struct json_object *more_obj = json_object_new_boolean(true);
if (req == NULL || method_obj == NULL || more_obj == NULL) {
return NULL;
}
json_object_object_add(req, "method", method_obj);
json_object_object_add(req, "parameters", in);
json_object_object_add(req, "more", more_obj);
if (!client_send(client, req)) {
return NULL;
}
struct vali_client_call *call = calloc(1, sizeof(*call));
if (call == NULL) {
return NULL;
}
call->client = client;
assert(client->pending_call == NULL);
client->pending_call = call;
return call;
}
bool vali_client_call_oneway(struct vali_client *client,
const char *method, struct json_object *in) {
assert(in == NULL || json_object_get_type(in) == json_type_object);
struct json_object *req = json_object_new_object();
struct json_object *method_obj = json_object_new_string(method);
struct json_object *oneway_obj = json_object_new_boolean(true);
if (req == NULL || method_obj == NULL || oneway_obj == NULL) {
return false;
}
json_object_object_add(req, "method", method_obj);
json_object_object_add(req, "parameters", in);
json_object_object_add(req, "oneway", oneway_obj);
return client_send(client, req);
}
void vali_error_finish(struct vali_error *err) {
free(err->name);
json_object_put(err->parameters);
}
void vali_error_set(struct vali_error *err, const char *name,
struct json_object *parameters) {
vali_error_finish(err);
*err = (struct vali_error){
.name = strdup(name),
.parameters = json_object_get(parameters),
};
}
bool vali_client_call_wait(struct vali_client_call *call,
struct json_object **out, struct vali_error *err) {
assert(!vali_client_call_is_done(call));
bool continues = false;
if (!client_receive(call->client, out, err, &continues)) {
return false;
}
if (!continues) {
call->client->pending_call = NULL;
}
return true;
}
void vali_client_call_destroy(struct vali_client_call *call) {
assert(vali_client_call_is_done(call));
free(call);
}
bool vali_client_call_is_done(struct vali_client_call *call) {
return call->client->pending_call != call;
}
|