GCC Code Coverage Report


src/
File: src/service/service.c
Date: 2026-04-08 15:21:49
Lines:
74/122
60.7%
Functions:
2/2
100.0%
Branches:
51/97
52.6%

Line Branch Exec Source
1 #include <glib.h>
2 #include <inttypes.h>
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <gio/gio.h>
8 #include "service/service.h"
9 #include "service/service_internals.h"
10
11 /**
12 * @brief Thread function to handle a client connection to the service
13 *
14 * @param data The client context
15 * @return gpointer NULL
16 */
17 2 static gpointer handle_client(gpointer data) {
18 2 GSocket* client = ((client_thread_data_t*)data)->client;
19 2 bool run_thread = true;
20 2 void* buffer = NULL;
21
22
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 while (run_thread) {
23 // Parse the header
24 header_t header;
25 3 GError* error = NULL;
26
1/1
✓ Branch 1 taken 3 times.
3 g_socket_receive(client, (void*)&header, sizeof(header), NULL, &error);
27
28 // Error handle
29
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (error) {
30 g_printerr("[GitTor Service thread=%p] Receive header failed: %s\n",
31 (void*)g_thread_self(), error->message);
32 g_clear_error(&error);
33 break;
34 }
35
36 // Check the header signature
37
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (header.magic != MAGIC) {
38 g_printerr(
39 "[GitTor Service thread=%p] Received incorrect header "
40 "signiture: got '%" PRIx64 "' expected '%" PRIx64 "'\n",
41 (void*)g_thread_self(), header.magic, MAGIC);
42 break;
43 }
44
45 // Recieve the body
46 3 free(buffer);
47 3 buffer = NULL;
48
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (header.len > 0) {
49 1 buffer = malloc(header.len);
50
1/1
✓ Branch 1 taken 1 times.
1 g_socket_receive(client, buffer, header.len, NULL, &error);
51 }
52
53 // Error handle
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (error) {
55 g_printerr("[GitTor Service thread=%p] Receive failed: %s\n",
56 (void*)g_thread_self(), error->message);
57 g_clear_error(&error);
58 break;
59 }
60
61 // Handle the message
62
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
3 switch (header.type) {
63 1 case KILL:
64 1 run_thread = false;
65
1/1
✓ Branch 1 taken 1 times.
1 g_cancellable_cancel(
66 ((client_thread_data_t*)data)->connection_cancellable);
67 3 break;
68 1 case END:
69 1 run_thread = false;
70 1 break;
71 1 case PING:
72 1 printf("[GitTor Service thread=%p] Recieved: '%s'\n",
73
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 (void*)g_thread_self(), (char*)buffer);
74 char ping[256];
75 1 header.len =
76
1/1
✓ Branch 1 taken 1 times.
1 g_snprintf(ping, sizeof(ping) - 1, "pid: %d thread: %p",
77
1/1
✓ Branch 1 taken 1 times.
1 getpid(), (void*)g_thread_self()) +
78 1;
79 1 header.type = PING;
80
1/1
✓ Branch 1 taken 1 times.
1 g_socket_send(client, (void*)&header, sizeof(header), NULL,
81 &error);
82
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (error) {
83 g_printerr(
84 "[GitTor Service thread=%p] Reply body failed: %s\n",
85 (void*)g_thread_self(), error->message);
86 run_thread = false;
87 break;
88 }
89
90
1/1
✓ Branch 1 taken 1 times.
1 g_socket_send(client, (void*)ping, header.len, NULL, &error);
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (error) {
92 g_printerr(
93 "[GitTor Service thread=%p] Reply body failed: %s\n",
94 (void*)g_thread_self(), error->message);
95 run_thread = false;
96 break;
97 }
98 1 break;
99 }
100 }
101
102 2 free(buffer);
103 2 g_object_unref(client);
104 2 g_free(data);
105 2 return NULL;
106 }
107
108 1 extern int gittor_service_main() {
109 // Notify that the service is not up
110 1 GError* error = NULL;
111
1/1
✓ Branch 1 taken 1 times.
1 gittor_service_set_port(-1, &error);
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (error) {
113 g_printerr("[GitTor Service] Clear port configuration failed: %s\n",
114 error->message);
115 g_clear_error(&error);
116 // No need to throw error, things will still work just not as well
117 }
118
119 // Create an IPv4 TCP socket
120
1/1
✓ Branch 1 taken 1 times.
1 GSocket* socket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM,
121 G_SOCKET_PROTOCOL_TCP, &error);
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (error) {
123 g_printerr("[GitTor Service] Error creating socket: %s\n",
124 error->message);
125 g_clear_error(&error);
126 g_object_unref(socket);
127 1 return 1;
128 }
129
130 // Bind to open port on localhost
131 1 GSocketAddress* address = NULL;
132
1/1
✓ Branch 1 taken 1 times.
1 int port = bind_port_in_range(&address, "127.0.0.1", socket, 5000, 6000);
133
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (port < 0) {
134 g_printerr("[GitTor Service] Bind failed: no valid free ports found\n");
135 g_clear_error(&error);
136 g_object_unref(socket);
137 g_object_unref(address);
138 return 1;
139 }
140
141 // Start listening
142
2/3
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (!g_socket_listen(socket, &error)) {
143 g_printerr("[GitTor Service] Listen failed: %s\n", error->message);
144 g_clear_error(&error);
145 g_object_unref(socket);
146 g_object_unref(address);
147 return 1;
148 }
149
150 // Notify that the service is up on this port
151
1/1
✓ Branch 1 taken 1 times.
1 gittor_service_set_port(port, &error);
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (error) {
153 g_printerr("[GitTor Service] Set port configuration failed: %s\n",
154 error->message);
155 g_clear_error(&error);
156 g_object_unref(socket);
157 g_object_unref(address);
158 return 1;
159 }
160
161 // Establish connections
162
1/1
✓ Branch 1 taken 1 times.
1 GPtrArray* threads = g_ptr_array_new();
163
1/1
✓ Branch 1 taken 1 times.
1 GCancellable* cancellable = g_cancellable_new();
164 2 while (true) {
165 // Accept a new connection
166
1/1
✓ Branch 1 taken 3 times.
3 GSocket* client = g_socket_accept(socket, cancellable, &error);
167
168
3/3
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
3 if (g_cancellable_is_cancelled(cancellable)) {
169
1/1
✓ Branch 1 taken 1 times.
1 g_clear_error(&error);
170 1 break;
171 }
172
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (error) {
174 g_printerr("[GitTor Service] Accept failed: %s\n", error->message);
175 g_clear_error(&error);
176 continue;
177 }
178
179 // Spawn a thread for the new client
180
1/1
✓ Branch 1 taken 2 times.
2 client_thread_data_t* data = g_malloc(sizeof(*data));
181 2 data->client = client;
182 2 data->connection_cancellable = cancellable;
183 2 g_ptr_array_add(threads,
184
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 g_thread_new("client-handler", handle_client, data));
185 }
186
187 // Notify that the service is not up
188
1/1
✓ Branch 1 taken 1 times.
1 gittor_service_set_port(-1, &error);
189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (error) {
190 g_printerr("[GitTor Service] Clear port configuration failed: %s\n",
191 error->message);
192 g_clear_error(&error);
193 // No need to error, things will still work just not as well
194 }
195
196 // Cleanup all threads
197
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (guint i = 0; i < threads->len; i++) {
198
1/1
✓ Branch 1 taken 2 times.
2 g_thread_join(g_ptr_array_index(threads, i));
199 }
200
201
1/1
✓ Branch 1 taken 1 times.
1 g_ptr_array_free(threads, true);
202
1/1
✓ Branch 1 taken 1 times.
1 g_object_unref(socket);
203
1/1
✓ Branch 1 taken 1 times.
1 g_object_unref(address);
204
1/1
✓ Branch 1 taken 1 times.
1 g_object_unref(cancellable);
205 1 return 0;
206 }
207
208 #ifndef MOCK_GITTOR_SERVICE_RUN
209 extern int gittor_service_run(bool detached) {
210 if (!detached) {
211 return gittor_service_main();
212 }
213
214 // Notify that the service is not up
215 GError* error = NULL;
216 gittor_service_set_port(-1, &error);
217 if (error) {
218 g_printerr("Clear port configuration failed: %s\n", error->message);
219 g_clear_error(&error);
220 // No need to throw error, things will still work just not as well
221 }
222
223 const gchar* argv[] = {g_get_prgname(), "service", "run", NULL};
224 g_spawn_async(NULL, (gchar**)argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
225 NULL, &error);
226 if (error) {
227 g_printerr("Failed to start service: %s\n", error->message);
228 g_clear_error(&error);
229 return 1;
230 }
231
232 // While port is invalid, wait
233 int count = 0;
234 while (count < 20) {
235 int port = gittor_service_get_port(&error);
236 if (!error && port > 0) {
237 break;
238 }
239 if (error) {
240 g_clear_error(&error);
241 }
242 g_usleep(100UL * 1000UL); // 100 ms
243 count++;
244 }
245
246 return 0;
247 }
248 #endif
249