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