Blame


1 50794f47 2023-04-04 op /*
2 50794f47 2023-04-04 op * This file is in the public domain.
3 50794f47 2023-04-04 op */
4 50794f47 2023-04-04 op
5 50794f47 2023-04-04 op #include <sys/socket.h>
6 50794f47 2023-04-04 op #include <sys/stat.h>
7 50794f47 2023-04-04 op #include <sys/tree.h>
8 50794f47 2023-04-04 op #include <sys/types.h>
9 50794f47 2023-04-04 op #include <sys/un.h>
10 50794f47 2023-04-04 op #include <sys/wait.h>
11 50794f47 2023-04-04 op
12 50794f47 2023-04-04 op #include <err.h>
13 50794f47 2023-04-04 op #include <errno.h>
14 50794f47 2023-04-04 op #include <event.h>
15 50794f47 2023-04-04 op #include <fcntl.h>
16 50794f47 2023-04-04 op #include <limits.h>
17 50794f47 2023-04-04 op #include <pwd.h>
18 50794f47 2023-04-04 op #include <signal.h>
19 50794f47 2023-04-04 op #include <stdarg.h>
20 50794f47 2023-04-04 op #include <stdio.h>
21 50794f47 2023-04-04 op #include <stdlib.h>
22 50794f47 2023-04-04 op #include <string.h>
23 50794f47 2023-04-04 op #include <syslog.h>
24 50794f47 2023-04-04 op #include <unistd.h>
25 50794f47 2023-04-04 op
26 dded5403 2023-10-16 op #include "log.h"
27 50794f47 2023-04-04 op #include "msearchd.h"
28 50794f47 2023-04-04 op
29 50794f47 2023-04-04 op #ifndef MSEARCHD_DB
30 50794f47 2023-04-04 op #define MSEARCHD_DB "/msearchd/mails.sqlite3"
31 50794f47 2023-04-04 op #endif
32 50794f47 2023-04-04 op
33 50794f47 2023-04-04 op #ifndef MSEARCHD_SOCK
34 50794f47 2023-04-04 op #define MSEARCHD_SOCK "/run/msearchd.sock"
35 50794f47 2023-04-04 op #endif
36 50794f47 2023-04-04 op
37 50794f47 2023-04-04 op #ifndef MSEARCHD_USER
38 50794f47 2023-04-04 op #define MSEARCHD_USER "www"
39 50794f47 2023-04-04 op #endif
40 50794f47 2023-04-04 op
41 8e5ae9ac 2023-05-05 op #ifndef MSEARCH_TMPL_DIR
42 38232a0a 2023-05-07 op #define MSEARCH_TMPL_DIR SYSCONFDIR "/smarc"
43 8e5ae9ac 2023-05-05 op #endif
44 8e5ae9ac 2023-05-05 op
45 50794f47 2023-04-04 op #define MAX_CHILDREN 32
46 50794f47 2023-04-04 op
47 50794f47 2023-04-04 op int debug;
48 50794f47 2023-04-04 op int verbose;
49 50794f47 2023-04-04 op int children = 3;
50 50794f47 2023-04-04 op pid_t pids[MAX_CHILDREN];
51 50794f47 2023-04-04 op
52 8e5ae9ac 2023-05-05 op const char *tmpl_head;
53 8e5ae9ac 2023-05-05 op const char *tmpl_search;
54 8e5ae9ac 2023-05-05 op const char *tmpl_search_header;
55 8e5ae9ac 2023-05-05 op const char *tmpl_foot;
56 50794f47 2023-04-04 op
57 50794f47 2023-04-04 op static void
58 28c73ba9 2023-04-04 op sighdlr(int sig)
59 50794f47 2023-04-04 op {
60 28c73ba9 2023-04-04 op static volatile sig_atomic_t got_sig;
61 50794f47 2023-04-04 op int i, save_errno;
62 50794f47 2023-04-04 op
63 28c73ba9 2023-04-04 op if (got_sig)
64 50794f47 2023-04-04 op return;
65 28c73ba9 2023-04-04 op got_sig = -1;
66 50794f47 2023-04-04 op
67 50794f47 2023-04-04 op save_errno = errno;
68 50794f47 2023-04-04 op for (i = 0; i < children; ++i)
69 50794f47 2023-04-04 op (void)kill(pids[i], SIGTERM);
70 50794f47 2023-04-04 op errno = save_errno;
71 50794f47 2023-04-04 op }
72 50794f47 2023-04-04 op
73 8e5ae9ac 2023-05-05 op static void
74 8e5ae9ac 2023-05-05 op load_tmpl(const char **ret, const char *dir, const char *name)
75 8e5ae9ac 2023-05-05 op {
76 8e5ae9ac 2023-05-05 op FILE *fp;
77 8e5ae9ac 2023-05-05 op struct stat sb;
78 8e5ae9ac 2023-05-05 op char *t;
79 8e5ae9ac 2023-05-05 op char path[PATH_MAX];
80 8e5ae9ac 2023-05-05 op int r;
81 8e5ae9ac 2023-05-05 op
82 8e5ae9ac 2023-05-05 op r = snprintf(path, sizeof(path), "%s/%s", dir, name);
83 8e5ae9ac 2023-05-05 op if (r < 0 || (size_t)r >= sizeof(path))
84 8e5ae9ac 2023-05-05 op fatalx("path too long: %s/%s", dir, name);
85 8e5ae9ac 2023-05-05 op
86 8e5ae9ac 2023-05-05 op if ((fp = fopen(path, "r")) == NULL)
87 8e5ae9ac 2023-05-05 op fatal("can't open %s", path);
88 8e5ae9ac 2023-05-05 op
89 8e5ae9ac 2023-05-05 op if (fstat(fileno(fp), &sb) == -1)
90 8e5ae9ac 2023-05-05 op fatal("fstat");
91 8e5ae9ac 2023-05-05 op
92 8e5ae9ac 2023-05-05 op if (sb.st_size > SIZE_MAX)
93 8e5ae9ac 2023-05-05 op fatal("file too big %s", path);
94 8e5ae9ac 2023-05-05 op
95 8e5ae9ac 2023-05-05 op if ((t = malloc(sb.st_size + 1)) == NULL)
96 8e5ae9ac 2023-05-05 op fatal("malloc");
97 8e5ae9ac 2023-05-05 op
98 8e5ae9ac 2023-05-05 op if (fread(t, 1, sb.st_size, fp) != sb.st_size)
99 8e5ae9ac 2023-05-05 op fatal("fread %s", path);
100 8e5ae9ac 2023-05-05 op
101 8e5ae9ac 2023-05-05 op fclose(fp);
102 8e5ae9ac 2023-05-05 op
103 8e5ae9ac 2023-05-05 op t[sb.st_size] = '\0';
104 8e5ae9ac 2023-05-05 op *ret = t;
105 8e5ae9ac 2023-05-05 op }
106 8e5ae9ac 2023-05-05 op
107 50794f47 2023-04-04 op static int
108 50794f47 2023-04-04 op bind_socket(const char *path, struct passwd *pw)
109 50794f47 2023-04-04 op {
110 50794f47 2023-04-04 op struct sockaddr_un sun;
111 50794f47 2023-04-04 op int fd, old_umask;
112 50794f47 2023-04-04 op
113 50794f47 2023-04-04 op if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
114 50794f47 2023-04-04 op log_warn("%s: socket", __func__);
115 50794f47 2023-04-04 op return (-1);
116 50794f47 2023-04-04 op }
117 50794f47 2023-04-04 op
118 50794f47 2023-04-04 op memset(&sun, 0, sizeof(sun));
119 50794f47 2023-04-04 op sun.sun_family = AF_UNIX;
120 50794f47 2023-04-04 op
121 50794f47 2023-04-04 op if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
122 50794f47 2023-04-04 op sizeof(sun.sun_path)) {
123 50794f47 2023-04-04 op log_warnx("%s: path too long: %s", __func__, path);
124 50794f47 2023-04-04 op close(fd);
125 50794f47 2023-04-04 op return (-1);
126 50794f47 2023-04-04 op }
127 50794f47 2023-04-04 op
128 50794f47 2023-04-04 op if (unlink(path) == -1 && errno != ENOENT) {
129 50794f47 2023-04-04 op log_warn("%s: unlink %s", __func__, path);
130 50794f47 2023-04-04 op close(fd);
131 50794f47 2023-04-04 op return (-1);
132 50794f47 2023-04-04 op }
133 50794f47 2023-04-04 op
134 50794f47 2023-04-04 op old_umask = umask(0117);
135 50794f47 2023-04-04 op if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
136 50794f47 2023-04-04 op log_warn("%s: bind: %s (%d)", __func__, path, geteuid());
137 50794f47 2023-04-04 op close(fd);
138 50794f47 2023-04-04 op umask(old_umask);
139 50794f47 2023-04-04 op return (-1);
140 50794f47 2023-04-04 op }
141 50794f47 2023-04-04 op umask(old_umask);
142 50794f47 2023-04-04 op
143 50794f47 2023-04-04 op if (chmod(path, 0660) == -1) {
144 50794f47 2023-04-04 op log_warn("%s: chmod 0660 %s", __func__, path);
145 50794f47 2023-04-04 op close(fd);
146 50794f47 2023-04-04 op (void)unlink(path);
147 50794f47 2023-04-04 op return (-1);
148 50794f47 2023-04-04 op }
149 50794f47 2023-04-04 op
150 50794f47 2023-04-04 op if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
151 50794f47 2023-04-04 op log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
152 50794f47 2023-04-04 op close(fd);
153 50794f47 2023-04-04 op (void)unlink(path);
154 50794f47 2023-04-04 op return (-1);
155 50794f47 2023-04-04 op }
156 50794f47 2023-04-04 op
157 50794f47 2023-04-04 op if (listen(fd, 5) == -1) {
158 50794f47 2023-04-04 op log_warn("%s: listen", __func__);
159 50794f47 2023-04-04 op close(fd);
160 50794f47 2023-04-04 op (void)unlink(path);
161 50794f47 2023-04-04 op return (-1);
162 50794f47 2023-04-04 op }
163 50794f47 2023-04-04 op
164 50794f47 2023-04-04 op return (fd);
165 50794f47 2023-04-04 op }
166 50794f47 2023-04-04 op
167 50794f47 2023-04-04 op static pid_t
168 50794f47 2023-04-04 op start_child(const char *argv0, const char *root, const char *user,
169 8e5ae9ac 2023-05-05 op const char *db, const char *tmpl, int debug, int verbose, int fd)
170 50794f47 2023-04-04 op {
171 8e5ae9ac 2023-05-05 op const char *argv[13];
172 50794f47 2023-04-04 op int argc = 0;
173 50794f47 2023-04-04 op pid_t pid;
174 50794f47 2023-04-04 op
175 50794f47 2023-04-04 op switch (pid = fork()) {
176 50794f47 2023-04-04 op case -1:
177 50794f47 2023-04-04 op fatal("cannot fork");
178 50794f47 2023-04-04 op case 0:
179 50794f47 2023-04-04 op break;
180 50794f47 2023-04-04 op default:
181 50794f47 2023-04-04 op close(fd);
182 50794f47 2023-04-04 op return (pid);
183 50794f47 2023-04-04 op }
184 50794f47 2023-04-04 op
185 50794f47 2023-04-04 op if (fd != 3) {
186 50794f47 2023-04-04 op if (dup2(fd, 3) == -1)
187 50794f47 2023-04-04 op fatal("cannot setup socket fd");
188 50794f47 2023-04-04 op } else if (fcntl(fd, F_SETFD, 0) == -1)
189 50794f47 2023-04-04 op fatal("cannot setup socket fd");
190 50794f47 2023-04-04 op
191 50794f47 2023-04-04 op argv[argc++] = argv0;
192 50794f47 2023-04-04 op argv[argc++] = "-S";
193 50794f47 2023-04-04 op argv[argc++] = "-p"; argv[argc++] = root;
194 8e5ae9ac 2023-05-05 op argv[argc++] = "-t"; argv[argc++] = tmpl;
195 50794f47 2023-04-04 op argv[argc++] = "-u"; argv[argc++] = user;
196 50794f47 2023-04-04 op if (debug)
197 50794f47 2023-04-04 op argv[argc++] = "-d";
198 50794f47 2023-04-04 op if (verbose--)
199 50794f47 2023-04-04 op argv[argc++] = "-v";
200 50794f47 2023-04-04 op if (verbose--)
201 50794f47 2023-04-04 op argv[argc++] = "-v";
202 50794f47 2023-04-04 op argv[argc++] = db;
203 50794f47 2023-04-04 op argv[argc++] = NULL;
204 50794f47 2023-04-04 op
205 50794f47 2023-04-04 op /* obnoxious cast */
206 50794f47 2023-04-04 op execvp(argv0, (char * const *) argv);
207 50794f47 2023-04-04 op fatal("execvp %s", argv0);
208 50794f47 2023-04-04 op }
209 50794f47 2023-04-04 op
210 50794f47 2023-04-04 op static void __dead
211 50794f47 2023-04-04 op usage(void)
212 50794f47 2023-04-04 op {
213 8e5ae9ac 2023-05-05 op fprintf(stderr, "usage: %s [-dv] [-j n] [-p path] [-s socket]"
214 8e5ae9ac 2023-05-05 op " [-t tmpldir] [-u user] [db]\n",
215 50794f47 2023-04-04 op getprogname());
216 50794f47 2023-04-04 op exit(1);
217 50794f47 2023-04-04 op }
218 50794f47 2023-04-04 op
219 50794f47 2023-04-04 op int
220 50794f47 2023-04-04 op main(int argc, char **argv)
221 50794f47 2023-04-04 op {
222 50794f47 2023-04-04 op struct stat sb;
223 50794f47 2023-04-04 op struct passwd *pw;
224 50794f47 2023-04-04 op char sockp[PATH_MAX];
225 50794f47 2023-04-04 op const char *sock = MSEARCHD_SOCK;
226 50794f47 2023-04-04 op const char *user = MSEARCHD_USER;
227 50794f47 2023-04-04 op const char *root = NULL;
228 50794f47 2023-04-04 op const char *db = MSEARCHD_DB;
229 8e5ae9ac 2023-05-05 op const char *tmpldir = MSEARCH_TMPL_DIR;
230 50794f47 2023-04-04 op const char *errstr, *cause, *argv0;
231 50794f47 2023-04-04 op pid_t pid;
232 50794f47 2023-04-04 op int ch, i, fd, ret, status, server = 0;
233 50794f47 2023-04-04 op
234 50794f47 2023-04-04 op /*
235 50794f47 2023-04-04 op * Ensure we have fds 0-2 open so that we have no issue with
236 50794f47 2023-04-04 op * calling bind_socket before daemon(3).
237 50794f47 2023-04-04 op */
238 50794f47 2023-04-04 op for (i = 0; i < 3; ++i) {
239 50794f47 2023-04-04 op if (fstat(i, &sb) == -1) {
240 50794f47 2023-04-04 op if ((fd = open("/dev/null", O_RDWR)) != -1) {
241 50794f47 2023-04-04 op if (dup2(fd, i) == -1)
242 50794f47 2023-04-04 op exit(1);
243 50794f47 2023-04-04 op if (fd > i)
244 50794f47 2023-04-04 op close(fd);
245 50794f47 2023-04-04 op } else
246 50794f47 2023-04-04 op exit(1);
247 50794f47 2023-04-04 op }
248 50794f47 2023-04-04 op }
249 50794f47 2023-04-04 op
250 50794f47 2023-04-04 op if ((argv0 = argv[0]) == NULL)
251 50794f47 2023-04-04 op argv0 = "msearchd";
252 50794f47 2023-04-04 op
253 8e5ae9ac 2023-05-05 op while ((ch = getopt(argc, argv, "dj:p:Ss:t:u:v")) != -1) {
254 50794f47 2023-04-04 op switch (ch) {
255 50794f47 2023-04-04 op case 'd':
256 50794f47 2023-04-04 op debug = 1;
257 50794f47 2023-04-04 op break;
258 50794f47 2023-04-04 op case 'j':
259 50794f47 2023-04-04 op children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
260 50794f47 2023-04-04 op if (errstr)
261 50794f47 2023-04-04 op fatalx("number of children is %s: %s",
262 50794f47 2023-04-04 op errstr, optarg);
263 50794f47 2023-04-04 op break;
264 50794f47 2023-04-04 op case 'p':
265 50794f47 2023-04-04 op root = optarg;
266 50794f47 2023-04-04 op break;
267 50794f47 2023-04-04 op case 'S':
268 50794f47 2023-04-04 op server = 1;
269 50794f47 2023-04-04 op break;
270 50794f47 2023-04-04 op case 's':
271 50794f47 2023-04-04 op sock = optarg;
272 50794f47 2023-04-04 op break;
273 8e5ae9ac 2023-05-05 op case 't':
274 8e5ae9ac 2023-05-05 op tmpldir = optarg;
275 8e5ae9ac 2023-05-05 op break;
276 50794f47 2023-04-04 op case 'u':
277 50794f47 2023-04-04 op user = optarg;
278 50794f47 2023-04-04 op break;
279 50794f47 2023-04-04 op case 'v':
280 50794f47 2023-04-04 op verbose++;
281 50794f47 2023-04-04 op break;
282 50794f47 2023-04-04 op default:
283 50794f47 2023-04-04 op usage();
284 50794f47 2023-04-04 op }
285 50794f47 2023-04-04 op }
286 50794f47 2023-04-04 op argc -= optind;
287 50794f47 2023-04-04 op argv += optind;
288 50794f47 2023-04-04 op
289 50794f47 2023-04-04 op if (argc > 0) {
290 50794f47 2023-04-04 op db = argv[0];
291 50794f47 2023-04-04 op argv++;
292 50794f47 2023-04-04 op argc--;
293 50794f47 2023-04-04 op }
294 50794f47 2023-04-04 op if (argc != 0)
295 50794f47 2023-04-04 op usage();
296 50794f47 2023-04-04 op
297 50794f47 2023-04-04 op if (geteuid())
298 50794f47 2023-04-04 op fatalx("need root privileges");
299 50794f47 2023-04-04 op
300 50794f47 2023-04-04 op pw = getpwnam(user);
301 50794f47 2023-04-04 op if (pw == NULL)
302 50794f47 2023-04-04 op fatalx("user %s not found", user);
303 50794f47 2023-04-04 op if (pw->pw_uid == 0)
304 50794f47 2023-04-04 op fatalx("cannot run as %s: must not be the superuser", user);
305 50794f47 2023-04-04 op
306 50794f47 2023-04-04 op if (root == NULL)
307 50794f47 2023-04-04 op root = pw->pw_dir;
308 2901dc0e 2023-04-04 op
309 dded5403 2023-10-16 op log_init(debug, LOG_DAEMON);
310 2901dc0e 2023-04-04 op
311 2901dc0e 2023-04-04 op if (!debug && !server && daemon(1, 0) == -1)
312 2901dc0e 2023-04-04 op fatal("daemon");
313 50794f47 2023-04-04 op
314 50794f47 2023-04-04 op if (!server) {
315 50794f47 2023-04-04 op sigset_t set;
316 50794f47 2023-04-04 op
317 50794f47 2023-04-04 op sigemptyset(&set);
318 50794f47 2023-04-04 op sigaddset(&set, SIGCHLD);
319 28c73ba9 2023-04-04 op sigaddset(&set, SIGINT);
320 28c73ba9 2023-04-04 op sigaddset(&set, SIGTERM);
321 50794f47 2023-04-04 op sigprocmask(SIG_BLOCK, &set, NULL);
322 50794f47 2023-04-04 op
323 50794f47 2023-04-04 op ret = snprintf(sockp, sizeof(sockp), "%s/%s", root, sock);
324 50794f47 2023-04-04 op if (ret < 0 || (size_t)ret >= sizeof(sockp))
325 50794f47 2023-04-04 op fatalx("socket path too long");
326 50794f47 2023-04-04 op if ((fd = bind_socket(sockp, pw)) == -1)
327 50794f47 2023-04-04 op fatalx("failed to open socket %s", sock);
328 50794f47 2023-04-04 op for (i = 0; i < children; ++i) {
329 50794f47 2023-04-04 op int d;
330 50794f47 2023-04-04 op
331 50794f47 2023-04-04 op if ((d = dup(fd)) == -1)
332 50794f47 2023-04-04 op fatalx("dup");
333 8e5ae9ac 2023-05-05 op pids[i] = start_child(argv0, root, user, db, tmpldir,
334 8e5ae9ac 2023-05-05 op debug, verbose, d);
335 50794f47 2023-04-04 op log_debug("forking child %d (pid %lld)", i,
336 50794f47 2023-04-04 op (long long)pids[i]);
337 50794f47 2023-04-04 op }
338 50794f47 2023-04-04 op
339 28c73ba9 2023-04-04 op signal(SIGINT, sighdlr);
340 28c73ba9 2023-04-04 op signal(SIGTERM, sighdlr);
341 28c73ba9 2023-04-04 op signal(SIGCHLD, sighdlr);
342 50794f47 2023-04-04 op signal(SIGHUP, SIG_IGN);
343 50794f47 2023-04-04 op
344 50794f47 2023-04-04 op sigprocmask(SIG_UNBLOCK, &set, NULL);
345 8e5ae9ac 2023-05-05 op } else {
346 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_head, tmpldir, "head.html");
347 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_search, tmpldir, "search.html");
348 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_search_header, tmpldir, "search-header.html");
349 8e5ae9ac 2023-05-05 op load_tmpl(&tmpl_foot, tmpldir, "foot.html");
350 ab1569ee 2023-05-06 op
351 ab1569ee 2023-05-06 op setproctitle("server");
352 50794f47 2023-04-04 op }
353 50794f47 2023-04-04 op
354 50794f47 2023-04-04 op if (chroot(root) == -1)
355 50794f47 2023-04-04 op fatal("chroot %s", root);
356 50794f47 2023-04-04 op if (chdir("/") == -1)
357 50794f47 2023-04-04 op fatal("chdir /");
358 50794f47 2023-04-04 op
359 50794f47 2023-04-04 op if (setgroups(1, &pw->pw_gid) == -1 ||
360 50794f47 2023-04-04 op setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
361 50794f47 2023-04-04 op setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
362 50794f47 2023-04-04 op fatal("failed to drop privileges");
363 50794f47 2023-04-04 op
364 50794f47 2023-04-04 op if (server)
365 50794f47 2023-04-04 op return (server_main(db));
366 50794f47 2023-04-04 op
367 50794f47 2023-04-04 op if (pledge("stdio proc", NULL) == -1)
368 50794f47 2023-04-04 op fatal("pledge");
369 50794f47 2023-04-04 op
370 50794f47 2023-04-04 op for (;;) {
371 50794f47 2023-04-04 op do {
372 50794f47 2023-04-04 op pid = waitpid(WAIT_ANY, &status, 0);
373 50794f47 2023-04-04 op } while (pid != -1 || errno == EINTR);
374 50794f47 2023-04-04 op
375 50794f47 2023-04-04 op if (pid == -1) {
376 50794f47 2023-04-04 op if (errno == ECHILD)
377 50794f47 2023-04-04 op break;
378 50794f47 2023-04-04 op fatal("waitpid");
379 50794f47 2023-04-04 op }
380 50794f47 2023-04-04 op
381 50794f47 2023-04-04 op if (WIFSIGNALED(status))
382 50794f47 2023-04-04 op cause = "was terminated";
383 50794f47 2023-04-04 op else if (WIFEXITED(status)) {
384 50794f47 2023-04-04 op if (WEXITSTATUS(status) != 0)
385 50794f47 2023-04-04 op cause = "exited abnormally";
386 50794f47 2023-04-04 op else
387 50794f47 2023-04-04 op cause = "exited successfully";
388 50794f47 2023-04-04 op } else
389 50794f47 2023-04-04 op cause = "died";
390 50794f47 2023-04-04 op
391 50794f47 2023-04-04 op log_warnx("child process %lld %s", (long long)pid, cause);
392 50794f47 2023-04-04 op }
393 50794f47 2023-04-04 op
394 50794f47 2023-04-04 op return (1);
395 50794f47 2023-04-04 op }