-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathgr_api.h
More file actions
238 lines (203 loc) · 9.19 KB
/
Copy pathgr_api.h
File metadata and controls
238 lines (203 loc) · 9.19 KB
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry
#pragma once
#include <assert.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
// Must be bumped when making non-backward compatible changes in API headers
#define GR_API_VERSION 4
// API request header.
struct gr_api_request {
uint32_t id; // Auto-generated by client
uint32_t type;
uint32_t payload_len; // max GR_API_MAX_MSG_LEN
};
// API response header.
struct gr_api_response {
uint32_t for_id; // matches gr_api_request.id (for out-of-order handling)
uint32_t status; // errno values, 0 for success
uint32_t payload_len; // max GR_API_MAX_MSG_LEN
};
#define GR_API_MAX_MSG_LEN (128 * 1024)
#define GR_MSG_TYPE(module, id) (((uint32_t)(0xffff & module) << 16) | (0xffff & id))
#define GR_EVENT_ALL UINT32_C(0xffffffff)
#define GR_DEFAULT_SOCK_PATH "/run/grout.sock"
// Opaque API client handle (NOT thread-safe, use one per thread).
struct gr_api_client;
// Connect to the API server.
// Automatically sends GR_HELLO with version negotiation.
// Returns NULL on failure (check errno for details).
struct gr_api_client *gr_api_client_connect(const char *sock_path);
// Disconnect from the API server.
// Automatically unsubscribes from all events.
int gr_api_client_disconnect(struct gr_api_client *);
// Send an API request.
// Returns request ID on success, negative errno on failure.
// Client handles out-of-order responses automatically.
long int
gr_api_client_send(struct gr_api_client *, uint32_t req_type, size_t tx_len, const void *tx_data);
// Receive an API response with minimum payload size validation.
// Caller must free(*rx_data) after use.
// Returns 0 on success, negative errno on failure.
// Returns -EMSGSIZE if payload is non-empty but smaller than min_resp_size.
int gr_api_client_recv(struct gr_api_client *, uint32_t req_type, uint32_t for_id, void **rx_data);
// Send a request and receive the response.
// Validates response payload size against GR_REQ-declared type.
// Caller must free(*rx_data) after use.
// Returns 0 on success, negative errno on failure.
static inline int gr_api_client_send_recv(
struct gr_api_client *client,
uint32_t req_type,
size_t tx_len,
const void *tx_data,
void **rx_data
) {
long int ret = gr_api_client_send(client, req_type, tx_len, tx_data);
if (ret < 0)
return ret;
return gr_api_client_recv(client, req_type, ret, rx_data);
}
// internal, called when interrupting gr_api_client_stream_foreach()
int __gr_api_client_stream_drain(struct gr_api_client *, uint32_t req_type, uint32_t for_id);
// Send a request and iterate over the received stream of responses.
//
// @param obj Iterator variable (const pointer to response object type).
// @param ret Final return code of the operation.
// @param client API client handle.
// @param req_type Request type code.
// @param tx_len Request payload size (0 if tx_data is NULL).
// @param tx_data Request payload (NULL if tx_len is 0).
//
// This should be used like a for loop, e.g.:
//
// struct gr_iface_list_req req = {.type = GR_IFACE_TYPE_UNDEF};
// const struct gr_iface *iface;
// int ret;
//
// gr_api_client_stream_foreach (iface, ret, client, GR_IFACE_LIST, sizeof(req), &req)
// printf("Interface: %s\n", iface->name);
// if (ret < 0)
// handle_error(ret);
//
// The loop can be interrupted with break which drains remaining responses.
//
// XXX: Interrupting the loop with an early return will cause memory
// leaks and will leave messages hanging in the socket buffer.
#define gr_api_client_stream_foreach(obj, ret, client, req_type, tx_len, tx_data) \
for (long int __id = gr_api_client_send(client, req_type, tx_len, tx_data), \
__first = 1, \
__done = 0; \
({ \
if (__first == 1) { \
ret = __id; \
} else if (!__done && __id >= 0) { \
free((void *)obj); \
ret = __gr_api_client_stream_drain(client, req_type, __id); \
} \
__id >= 0 && __first; /* statement expression value */ \
}); \
__first = 0) \
for (void *__ptr = NULL; ({ \
bool more = false; \
ret = gr_api_client_recv(client, req_type, __id, &__ptr); \
if (ret < 0) { \
free(__ptr); \
__ptr = NULL; \
__done = 1; \
} else if (__ptr != NULL) { \
obj = __ptr; \
more = true; \
} else { \
__done = 1; \
} \
more; /* statement expression value */ \
}); \
free(__ptr), __ptr = NULL)
const char *gr_api_message_name(uint32_t type);
#ifndef GR_REQ
#define GR_REQ(code, req, resp) \
enum { \
code##_REQ_SIZE = sizeof(req), \
code##_RESP_SIZE = sizeof(resp) \
}
#endif
// Stream responses are always terminated by an empty response (header only with payload_len=0).
#ifndef GR_REQ_STREAM
#define GR_REQ_STREAM(code, req, resp) \
enum { \
code##_REQ_SIZE = sizeof(req), \
code##_RESP_SIZE = sizeof(resp) \
}
#endif
#ifndef GR_EVENT
#define GR_EVENT(code, obj) \
enum { \
code##_OBJ_SIZE = sizeof(obj) \
}
#endif
#ifndef GR_API_INLINE
#define GR_API_INLINE static inline
#endif
struct gr_empty { };
#define GR_MAIN_MODULE 0xcafe
enum gr_main_requests : uint32_t {
GR_HELLO = GR_MSG_TYPE(GR_MAIN_MODULE, 0x1981),
GR_LOG_PACKETS_SET,
GR_LOG_LEVEL_LIST,
GR_LOG_LEVEL_SET,
GR_EVENT_SUBSCRIBE,
GR_EVENT_UNSUBSCRIBE,
};
// Client handshake with API version negotiation.
// Must be the first request sent by any client.
// The api_version must match the server's GR_API_VERSION.
struct gr_hello_req {
uint32_t api_version; // must match GR_API_VERSION
char version[128]; // NUL-terminated
};
GR_REQ(GR_HELLO, struct gr_hello_req, struct gr_empty);
// Enable/disable packet ingress/egress logging.
struct gr_log_packets_set_req {
bool enabled;
};
GR_REQ(GR_LOG_PACKETS_SET, struct gr_log_packets_set_req, struct gr_empty);
struct gr_log_level_list_req {
bool show_all;
};
struct gr_log_entry {
char name[64];
uint32_t level;
};
GR_REQ_STREAM(GR_LOG_LEVEL_LIST, struct gr_log_level_list_req, struct gr_log_entry);
#define GR_LOG_LEVEL_MIN 1
#define GR_LOG_LEVEL_MAX 8
struct gr_log_level_set_req {
char pattern[64];
uint32_t level;
};
GR_REQ(GR_LOG_LEVEL_SET, struct gr_log_level_set_req, struct gr_empty);
// Subscribe to events of a given type.
// Use GR_EVENT_ALL to subscribe to all event types.
// Multiple subscriptions to same type update suppression flag.
struct gr_event_subscribe_req {
// Suppress events originating from API messages made by the same PID
// as the subscriber socket.
bool suppress_self_events;
uint32_t ev_type;
};
GR_REQ(GR_EVENT_SUBSCRIBE, struct gr_event_subscribe_req, struct gr_empty);
// Unsubscribe from all events.
// Removes ALL subscriptions for this client, not just specific types.
// Automatically called on client disconnection.
GR_REQ(GR_EVENT_UNSUBSCRIBE, struct gr_empty, struct gr_empty);
struct gr_api_event {
uint32_t ev_type;
size_t payload_len;
};
// Receive an event notification.
// Caller must free(*event) after use.
// Returns 0 on success, negative errno on failure.
int gr_api_client_event_recv(const struct gr_api_client *, struct gr_api_event **);