aboutsummaryrefslogtreecommitdiffhomepage
path: root/include/cli/ipc.h
blob: 6008db9872cc7bc706cb97b98dc5daa61a3ba856 (plain)
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
#ifndef GSR_CLI_IPC_H
#define GSR_CLI_IPC_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include <pthread.h>

#define GSR_IPC_MAX_CLIENTS 8
#define GSR_IPC_MAX_REQUEST_SIZE 4096
#define GSR_IPC_MAX_ERROR_MESSAGE_SIZE 256
#define GSR_IPC_MAX_ESCAPED_ERROR_MESSAGE_SIZE (GSR_IPC_MAX_ERROR_MESSAGE_SIZE*6)
#define GSR_IPC_MAX_ESCAPED_DATA_SIZE PATH_MAX
#define GSR_IPC_MAX_REPLY_SIZE (GSR_IPC_MAX_ESCAPED_ERROR_MESSAGE_SIZE + GSR_IPC_MAX_ESCAPED_DATA_SIZE + 128)
#define GSR_IPC_CLIENT_SEND_BUFFER_SIZE (GSR_IPC_MAX_REPLY_SIZE*2)

/*
    These are called from the ipc thread while the recording is running.
    Return false and write a message to |error_message| to reply to the request with an error.
    The reply to the requests that match a |gsr_ipc_deferred_request_type| is not sent when the handler
    succeeds. It's sent when the matching gsr_ipc_complete_request is called.
*/
typedef struct {
    bool (*stop)(char *error_message, size_t error_message_size, void *userdata);
    bool (*toggle_pause)(char *error_message, size_t error_message_size, void *userdata);
    bool (*set_paused)(bool paused, char *error_message, size_t error_message_size, void *userdata);
    bool (*toggle_replay_recording)(char *error_message, size_t error_message_size, void *userdata);
    bool (*start_replay_recording)(char *error_message, size_t error_message_size, void *userdata);
    bool (*stop_replay_recording)(char *error_message, size_t error_message_size, void *userdata);
    /* |seconds| is GSR_SAVE_REPLAY_SECONDS_FULL when the whole replay buffer should be saved */
    bool (*save_replay)(int seconds, char *error_message, size_t error_message_size, void *userdata);
    void *userdata;
} gsr_ipc_handlers;

/* Requests that are replied to when the file they save has been saved (see gsr_ipc_complete_request) */
typedef enum {
    GSR_IPC_DEFERRED_REQUEST_STOP,
    GSR_IPC_DEFERRED_REQUEST_SAVE_REPLAY,
    GSR_IPC_DEFERRED_REQUEST_STOP_REPLAY_RECORDING,
    GSR_IPC_DEFERRED_REQUEST_TYPE_COUNT
} gsr_ipc_deferred_request_type;

typedef enum {
    GSR_IPC_DEFERRED_REQUEST_STATE_EMPTY,
    GSR_IPC_DEFERRED_REQUEST_STATE_PENDING,
    GSR_IPC_DEFERRED_REQUEST_STATE_COMPLETED
} gsr_ipc_deferred_request_state;

typedef struct {
    gsr_ipc_deferred_request_state state;
    int client_fd;
    int64_t request_id;
    bool success;
    bool has_filepath;
    char filepath[PATH_MAX];
} gsr_ipc_deferred_request;

typedef struct {
    int fd;
    char request[GSR_IPC_MAX_REQUEST_SIZE];
    size_t request_size;
    bool request_too_large;
    char send_buffer[GSR_IPC_CLIENT_SEND_BUFFER_SIZE];
    size_t send_buffer_size;
} gsr_ipc_client;

/* Receives newline terminated json requests on a unix domain socket and replies to every request */
typedef struct {
    bool initialized;
    int socket_fd;
    int poll_fd;
    bool socket_bound;
    char socket_filepath[PATH_MAX];
    int wakeup_pipe[2];
    gsr_ipc_client clients[GSR_IPC_MAX_CLIENTS];
    int num_clients;
    gsr_ipc_handlers handlers;
    gsr_ipc_deferred_request deferred_requests[GSR_IPC_DEFERRED_REQUEST_TYPE_COUNT];
    pthread_mutex_t deferred_requests_mutex;
    bool deferred_requests_mutex_created;
    pthread_t thread;
    bool thread_running;
} gsr_ipc;

/* Returns a |gsr_error| value. Creates the socket, requests are not handled until gsr_ipc_start is called */
int gsr_ipc_init(gsr_ipc *self, const char *socket_filepath);
void gsr_ipc_deinit(gsr_ipc *self);

/* Returns a |gsr_error| value. Starts handling requests. Does nothing if |self| hasn't been initialized */
int gsr_ipc_start(gsr_ipc *self, const gsr_ipc_handlers *handlers);
/* Stops handling requests. Does nothing if the ipc hasn't been started */
void gsr_ipc_stop(gsr_ipc *self);

/*
    Sends the reply to the pending request of |type|, or does nothing when there is no pending request of that type.
    |filepath| is the path of the saved file and it can be NULL when nothing was saved.
    This is safe to call from any thread. Does nothing if |self| hasn't been initialized.
*/
void gsr_ipc_complete_request(gsr_ipc *self, gsr_ipc_deferred_request_type type, bool success, const char *filepath);

#endif /* GSR_CLI_IPC_H */