Dillo v3.2.0-151-g90488cbf
Loading...
Searching...
No Matches
dilloc.c
Go to the documentation of this file.
1/*
2 * File: dilloc.c
3 *
4 * Copyright 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
12#include "config.h"
13
14#include "dlib/dlib.h" /* dIsdigit */
15
16#include <dirent.h>
17#include <errno.h>
18#include <limits.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <sys/ioctl.h>
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/un.h>
26#include <unistd.h>
27#include <time.h>
28
29static int
30is_number(const char *str)
31{
32 for (const char *p = str; *p; p++) {
33 if (!dIsdigit(*p))
34 return 0;
35 }
36
37 return 1;
38}
39
40static double
42{
43 struct timespec ts;
44 clock_gettime(CLOCK_MONOTONIC, &ts);
45 return (double) ts.tv_sec + (double) ts.tv_nsec * 1.0e-9;
46}
47
48/* Connects to the given pid, retrying up to a timeout. */
49static int
50connect_given_pid(int *sock, const char *pid)
51{
52 struct sockaddr_un addr;
53 addr.sun_family = AF_UNIX;
54
55 int fd;
56 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
57 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
58 return -1;
59 }
60
61 char *path = addr.sun_path;
62 int len = (int) sizeof(addr.sun_path);
63 if (snprintf(path, len, "%s/.dillo/ctl/%s", dGethomedir(), pid) >= len) {
64 fprintf(stderr, "pid path too long\n");
65 return -1;
66 }
67
68 double t0 = get_time_s();
69 double maxtime = t0 + 10.0; /* 10 seconds */
70 double warntime = t0 + 1.0; /* 1 second */
71 /* Retry every 50 ms */
72 struct timespec dur = { .tv_sec = 0, .tv_nsec = 50UL * 1000UL * 1000UL };
73 int warned = 0;
74 while (1) {
75 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == 0) {
76 *sock = fd;
77 return 0;
78 } else if (errno != ENOENT) {
79 fprintf(stderr, "connect to %s failed: %s\n", path, strerror(errno));
80 return -1;
81 }
82
83 if (!warned && get_time_s() >= warntime) {
84 fprintf(stderr, "connect to %s taking long\n", path);
85 warned = 1;
86 }
87
88 if (get_time_s() >= maxtime) {
89 fprintf(stderr, "timeout connecting to %s: %s\n",
90 path, strerror(errno));
91 break;
92 }
93
94 /* Retry after a bit */
95 nanosleep(&dur, NULL);
96 }
97
98 dClose(fd);
99
100 return -1;
101}
102
103/* Try to locate dillo if there is only one process */
104static int
106{
107 char ctlpath[PATH_MAX];
108 if (snprintf(ctlpath, PATH_MAX, "%s/.dillo/ctl", dGethomedir()) >= PATH_MAX) {
109 fprintf(stderr, "path too long\n");
110 return -1;
111 }
112
113 struct dirent *ep;
114 DIR *dp = opendir(ctlpath);
115 if (dp == NULL) {
116 fprintf(stderr, "error: cannot open %s directory: %s\n",
117 ctlpath, strerror(errno));
118 fprintf(stderr, "hint: is dillo running?\n");
119 return -1;
120 }
121
122 int found_pid = 0;
123 struct sockaddr_un addr;
124 addr.sun_family = AF_UNIX;
125
126 while ((ep = readdir (dp)) != NULL) {
127 const char *num = ep->d_name;
128 /* Skip . and .. directories */
129 if (!strcmp(num, ".") || !strcmp(num, ".."))
130 continue;
131
132 /* Make sure it is only digits */
133 if (!is_number(num))
134 continue;
135
136#define LEN ((int) sizeof(addr.sun_path))
137 if (snprintf(addr.sun_path, LEN, "%s/%s", ctlpath, num) >= LEN) {
138 fprintf(stderr, "pid path too long\n");
139 return -1;
140 }
141#undef LEN
142
143 int fd;
144 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
145 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
146 return -1;
147 }
148
149 int ret;
150 do {
151 ret = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
152 } while (ret == -1 && errno == EINTR);
153
154 if (ret == 0) {
155 if (++found_pid == 1) {
156 *sock = fd; /* Found ok */
157 fd = -1;
158 /* Leave it open */
159 }
160 } else {
161 /* Remove the PID file if we cannot connect to it */
162 if (errno == ECONNREFUSED) {
163 /* Try to remove but don't check for errors */
164 remove(addr.sun_path);
165 } else {
166 fprintf(stderr, "cannot connect to %s, skipping: %s\n",
167 addr.sun_path, strerror(errno));
168 }
169 }
170
171 if (fd != -1 && dClose(fd) != 0) {
172 fprintf(stderr, "cannot close fd: %s", strerror(errno));
173 return -1;
174 }
175 }
176
177 closedir(dp);
178
179 if (found_pid == 1)
180 return 0;
181 else if (found_pid == 0) {
182 fprintf(stderr, "error: cannot find ctl socket, is dillo running?\n");
183 return -1;
184 }
185
186 fprintf(stderr, "multiple ctl sockets found, set DILLO_PID\n");
187 dClose(*sock);
188 return -1;
189}
190
191static int
193{
194 /* If the PID is given, use only that one */
195 char *given_pid = getenv("DILLO_PID");
196 int fd = -1;
197 if (given_pid) {
198 /* Connect to the given pid with retry (dillo may be starting) */
199 if (connect_given_pid(&fd, given_pid) != 0)
200 return -1;
201 } else {
202 /* Otherwise, try to find a working pid and remove those that don't work,
203 * which are likely dead processes */
204 if (find_working_socket(&fd) != 0)
205 return -1;
206 }
207
208 *sock = fd;
209
210 return 0;
211}
212
213int main(int argc, char *argv[])
214{
215 if (argc <= 1) {
216 fprintf(stderr, "missing command, try 'dilloc help'\n");
217 return 2;
218 }
219
220 int fd = -1;
221 if (connect_to_dillo(&fd) != 0)
222 return 2;
223
224 int is_load = 0;
225 if (argv[1] && (!strcmp(argv[1], "load") || !strcmp(argv[1], "rawload"))) {
226 is_load = 1;
227 }
228
229 Dstr *cmd = dStr_new("");
230 for (int i = 1; i < argc; i++) {
231 dStr_append(cmd, argv[i]);
232 if (i+1 < argc)
233 dStr_append_c(cmd, ' ');
234 }
235
236 dStr_append_c(cmd, '\n');
237
238 for (ssize_t w = 0; w < cmd->len; ) {
239 w = write(fd, cmd->str + w, cmd->len - w);
240 if (w < 0) {
241 perror("write command to socket failed");
242 return 2;
243 }
244 }
245
246 dStr_free(cmd, 1);
247
248 /* Forward stdin to the socket */
249 if (is_load) {
250 uint8_t buf[BUFSIZ];
251 while (1) {
252 ssize_t r = read(STDIN_FILENO, buf, BUFSIZ);
253 if (r == 0) {
254 break;
255 } else if (r < 0) {
256 perror("read stdin failed");
257 return 2;
258 }
259
260 uint8_t *p = buf;
261
262 while (r > 0) {
263 ssize_t w = write(fd, p, r);
264 if (w < 0) {
265 perror("write stdin to socket failed");
266 return 2;
267 }
268 r -= w;
269 p += w;
270 }
271 }
272
273 dClose(fd);
274 return 0;
275 }
276
277 /* First two bytes determine exit code */
278 int saw_rc = 0;
279 int rc = 2;
280
281 uint8_t buf[BUFSIZ];
282 while (1) {
283 ssize_t r = read(fd, buf, BUFSIZ);
284 if (r == 0) {
285 break;
286 } else if (r < 2) {
287 perror("read reply failed");
288 return 2;
289 }
290
291 uint8_t *p = buf;
292
293 if (!saw_rc) {
294 int ch = buf[0];
295 if (!isdigit(ch)) {
296 fprintf(stderr, "malformed reply: expected digit at first byte (got %c)\n", ch);
297 return 2;
298 }
299 if (buf[1] != '\n') {
300 fprintf(stderr, "malformed reply: expected newline at second byte\n");
301 return 2;
302 }
303 rc = (int) ch - '0';
304 r -= 2;
305 p += 2;
306 saw_rc = 1;
307 }
308
309 while (r > 0) {
310 ssize_t w = write(STDOUT_FILENO, p, r);
311 if (w < 0 && errno != EINTR) {
312 perror("cannot write reply to stdout");
313 return 2;
314 }
315 r -= w;
316 p += w;
317 }
318 }
319
320 close(fd);
321
322 return rc;
323}
int main(void)
Definition bookmarks.c:1612
static double get_time_s(void)
Definition dilloc.c:41
#define LEN
static int find_working_socket(int *sock)
Definition dilloc.c:105
static int is_number(const char *str)
Definition dilloc.c:30
static int connect_to_dillo(int *sock)
Definition dilloc.c:192
static int connect_given_pid(int *sock, const char *pid)
Definition dilloc.c:50
void dStr_append(Dstr *ds, const char *s)
Append a C string to a Dstr.
Definition dlib.c:315
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:336
int dClose(int fd)
Close a FD handling EINTR.
Definition dlib.c:978
void dStr_append_c(Dstr *ds, int c)
Append one character.
Definition dlib.c:348
Dstr * dStr_new(const char *s)
Create a new string.
Definition dlib.c:324
char * dGethomedir(void)
Return the home directory in a static string (don't free)
Definition dlib.c:933
static int dIsdigit(unsigned char c)
Definition dlib.h:50
#define BUFSIZ
Definition dlib.h:64
Definition dlib.h:131
Dstr_char_t * str
Definition dlib.h:134
int len
Definition dlib.h:133
Definition dpid.h:35
static void path()
Definition cookies.c:858