1 /* v/unix.c
2 **
3 ** This file is in the public domain.
4 */
5
6 #include "all.h"
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11 #include <sys/ioctl.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <setjmp.h>
15 #include <gmp.h>
16 #include <dirent.h>
17 #include <stdint.h>
18 #include <uv.h>
19 #include <termios.h>
20 #include <term.h>
21 #include <errno.h>
22 #include <libgen.h>
23 #include <ftw.h>
24
25 #include "vere/vere.h"
26
27 /* _unix_down(): descend path.
28 */
29 static c3_c*
_unix_down(c3_c * pax_c,c3_c * sub_c)30 _unix_down(c3_c* pax_c, c3_c* sub_c)
31 {
32 c3_w pax_w = strlen(pax_c);
33 c3_w sub_w = strlen(sub_c);
34 c3_c* don_c = c3_malloc(pax_w + sub_w + 2);
35
36 strncpy(don_c, pax_c, pax_w);
37 don_c[pax_w] = '/';
38 strncpy(don_c + pax_w + 1, sub_c, sub_w);
39 don_c[pax_w + 1 + sub_w] = '\0';
40
41 return don_c;
42 }
43
44 /* _unix_string_to_path(): convert c string to u3_noun path
45 *
46 * c string must begin with the pier path plus mountpoint
47 */
48 static u3_noun
_unix_string_to_path_helper(c3_c * pax_c)49 _unix_string_to_path_helper(c3_c* pax_c) {
50 c3_assert(pax_c[-1] == '/');
51 c3_c* end_w = strchr(pax_c, '/');
52 if ( !end_w ) {
53 end_w = strrchr(pax_c, '.');
54 if ( !end_w ) {
55 return u3nc(u3i_string(pax_c), u3_nul);
56 }
57 else {
58 return u3nt(u3i_bytes(end_w - pax_c, (c3_y*) pax_c),
59 u3i_string(end_w + 1),
60 u3_nul);
61 }
62 }
63 else {
64 return u3nc(u3i_bytes(end_w - pax_c, (c3_y*) pax_c),
65 _unix_string_to_path_helper(end_w + 1));
66 }
67 }
68 static u3_noun
_unix_string_to_path(c3_c * pax_c)69 _unix_string_to_path(c3_c* pax_c) {
70 pax_c += strlen(u3_Host.dir_c) + 1;
71 c3_c* pox_c = strchr(pax_c, '/');
72 if ( !pox_c ) {
73 pox_c = strchr(pax_c, '.');
74 if ( !pox_c ) {
75 return u3_nul;
76 }
77 else {
78 return u3nc(u3i_string(pox_c + 1), u3_nul);
79 }
80 }
81 else {
82 return _unix_string_to_path_helper(pox_c + 1);
83 }
84 }
85
86 /* _unix_rm_r_cb(): callback to delete individiual files/directories
87 */
88 static c3_i
_unix_rm_r_cb(const c3_c * pax_c,const struct stat * buf_u,c3_i typeflag,struct FTW * ftw_u)89 _unix_rm_r_cb(const c3_c* pax_c,
90 const struct stat* buf_u,
91 c3_i typeflag,
92 struct FTW* ftw_u)
93 {
94 switch ( typeflag ) {
95 default:
96 uL(fprintf(uH, "bad file type in rm_r: %s\r\n", pax_c));
97 break;
98 case FTW_F:
99 if ( 0 != unlink(pax_c) && ENOENT != errno ) {
100 uL(fprintf(uH, "error unlinking (in rm_r) %s: %s\n",
101 pax_c, strerror(errno)));
102 c3_assert(0);
103 }
104 break;
105 case FTW_D:
106 uL(fprintf(uH, "shouldn't have gotten pure directory: %s\r\n", pax_c));
107 break;
108 case FTW_DNR:
109 uL(fprintf(uH, "couldn't read directory: %s\r\n", pax_c));
110 break;
111 case FTW_NS:
112 uL(fprintf(uH, "couldn't stat path: %s\r\n", pax_c));
113 break;
114 case FTW_DP:
115 if ( 0 != rmdir(pax_c) && ENOENT != errno ) {
116 uL(fprintf(uH, "error rmdiring %s: %s\n", pax_c, strerror(errno)));
117 c3_assert(0);
118 }
119 break;
120 case FTW_SL:
121 uL(fprintf(uH, "got symbolic link: %s\r\n", pax_c));
122 break;
123 case FTW_SLN:
124 uL(fprintf(uH, "got nonexistent symbolic link: %s\r\n", pax_c));
125 break;
126 }
127
128 return 0;
129 }
130
131 /* _unix_rm_r(): rm -r directory
132 */
133 static void
_unix_rm_r(c3_c * pax_c)134 _unix_rm_r(c3_c* pax_c)
135 {
136 if ( 0 > nftw(pax_c, _unix_rm_r_cb, 100, FTW_DEPTH | FTW_PHYS )
137 && ENOENT != errno) {
138 uL(fprintf(uH, "rm_r error on %s: %s\r\n", pax_c, strerror(errno)));
139 }
140 }
141
142 /* _unix_mkdir(): mkdir, asserting.
143 */
144 static void
_unix_mkdir(c3_c * pax_c)145 _unix_mkdir(c3_c* pax_c)
146 {
147 if ( 0 != mkdir(pax_c, 0755) && EEXIST != errno) {
148 uL(fprintf(uH, "error mkdiring %s: %s\n", pax_c, strerror(errno)));
149 c3_assert(0);
150 }
151 }
152
153 /* _unix_write_file_hard(): write to a file, overwriting what's there
154 */
155 static c3_w
_unix_write_file_hard(c3_c * pax_c,u3_noun mim)156 _unix_write_file_hard(c3_c* pax_c, u3_noun mim)
157 {
158 c3_i fid_i = open(pax_c, O_WRONLY | O_CREAT | O_TRUNC, 0666);
159 c3_w len_w, rit_w, siz_w, mug_w = 0;
160 c3_y* dat_y;
161
162 u3_noun dat = u3t(u3t(mim));
163
164 if ( fid_i < 0 ) {
165 uL(fprintf(uH, "error opening %s for writing: %s\r\n",
166 pax_c, strerror(errno)));
167 u3z(mim);
168 return 0;
169 }
170
171 siz_w = u3h(u3t(mim));
172 len_w = u3r_met(3, dat);
173 dat_y = c3_malloc(siz_w);
174 memset(dat_y, 0, siz_w);
175
176 u3r_bytes(0, len_w, dat_y, dat);
177 u3z(mim);
178
179 rit_w = write(fid_i, dat_y, siz_w);
180
181 if ( rit_w != siz_w ) {
182 uL(fprintf(uH, "error writing %s: %s\r\n",
183 pax_c, strerror(errno)));
184 mug_w = 0;
185 }
186 else {
187 mug_w = u3r_mug_bytes(dat_y, len_w);
188 }
189
190 close(fid_i);
191 free(dat_y);
192
193 return mug_w;
194 }
195
196 /* _unix_write_file_soft(): write to a file, not overwriting if it's changed
197 */
198 static void
_unix_write_file_soft(u3_ufil * fil_u,u3_noun mim)199 _unix_write_file_soft(u3_ufil* fil_u, u3_noun mim)
200 {
201 struct stat buf_u;
202 c3_i fid_i = open(fil_u->pax_c, O_RDONLY, 0644);
203 c3_ws len_ws, red_ws;
204 c3_w old_w;
205 c3_y* old_y;
206
207 if ( fid_i < 0 || fstat(fid_i, &buf_u) < 0 ) {
208 if ( ENOENT == errno ) {
209 goto _unix_write_file_soft_go;
210 }
211 else {
212 uL(fprintf(uH, "error opening file (soft) %s: %s\r\n",
213 fil_u->pax_c, strerror(errno)));
214 u3z(mim);
215 return;
216 }
217 }
218
219 len_ws = buf_u.st_size;
220 old_y = c3_malloc(len_ws);
221
222 red_ws = read(fid_i, old_y, len_ws);
223
224 if ( close(fid_i) < 0 ) {
225 uL(fprintf(uH, "error closing file (soft) %s: %s\r\n",
226 fil_u->pax_c, strerror(errno)));
227 }
228
229 if ( len_ws != red_ws ) {
230 if ( red_ws < 0 ) {
231 uL(fprintf(uH, "error reading file (soft) %s: %s\r\n",
232 fil_u->pax_c, strerror(errno)));
233 }
234 else {
235 uL(fprintf(uH, "wrong # of bytes read in file %s: %d %d\r\n",
236 fil_u->pax_c, len_ws, red_ws));
237 }
238 free(old_y);
239 u3z(mim);
240 return;
241 }
242
243 old_w = u3r_mug_bytes(old_y, len_ws);
244
245 if ( old_w != fil_u->gum_w ) {
246 fil_u->gum_w = u3r_mug(u3t(u3t(mim))); // XXX this might fail with
247 free(old_y); // trailing zeros
248 u3z(mim);
249 return;
250 }
251
252 free(old_y);
253
254 _unix_write_file_soft_go:
255 fil_u->gum_w = _unix_write_file_hard(fil_u->pax_c, mim);
256 }
257
258 static void
259 _unix_watch_dir(u3_udir* dir_u, u3_udir* par_u, c3_c* pax_c);
260 static void
261 _unix_watch_file(u3_ufil* fil_u, u3_udir* par_u, c3_c* pax_c);
262
263 /* _unix_get_mount_point(): retrieve or create mount point
264 */
265 static u3_umon*
_unix_get_mount_point(u3_noun mon)266 _unix_get_mount_point(u3_noun mon)
267 {
268 if ( c3n == u3ud(mon) ) {
269 c3_assert(!"mount point must be an atom");
270 u3z(mon);
271 return NULL;
272 }
273
274 c3_c* nam_c = u3r_string(mon);
275 u3_umon* mon_u;
276
277 for ( mon_u = u3_Host.unx_u.mon_u;
278 mon_u && 0 != strcmp(nam_c, mon_u->nam_c);
279 mon_u = mon_u->nex_u )
280 {
281 }
282
283 if ( !mon_u ) {
284 mon_u = malloc(sizeof(u3_umon));
285 mon_u->nam_c = nam_c;
286 mon_u->dir_u.dir = c3y;
287 mon_u->dir_u.dry = c3n;
288 mon_u->dir_u.pax_c = strdup(u3_Host.dir_c);
289 mon_u->dir_u.par_u = NULL;
290 mon_u->dir_u.nex_u = NULL;
291 mon_u->dir_u.kid_u = NULL;
292 mon_u->nex_u = u3_Host.unx_u.mon_u;
293 u3_Host.unx_u.mon_u = mon_u;
294
295 }
296 else {
297 free(nam_c);
298 }
299
300 u3z(mon);
301
302 return mon_u;
303 }
304
305 /* _unix_scan_mount_point(): scan unix for already-existing mount point
306 */
307 static void
_unix_scan_mount_point(u3_umon * mon_u)308 _unix_scan_mount_point(u3_umon* mon_u)
309 {
310 DIR* rid_u = opendir(mon_u->dir_u.pax_c);
311 if ( !rid_u ) {
312 uL(fprintf(uH, "error opening pier directory: %s: %s\r\n",
313 mon_u->dir_u.pax_c, strerror(errno)));
314 return;
315 }
316
317 c3_w len_w = strlen(mon_u->nam_c);
318
319 while ( 1 ) {
320 struct dirent ent_u;
321 struct dirent* out_u;
322 c3_w err_w;
323
324 if ( 0 != (err_w = readdir_r(rid_u, &ent_u, &out_u)) ) {
325 uL(fprintf(uH, "erroring loading pier directory %s: %s\r\n",
326 mon_u->dir_u.pax_c, strerror(errno)));
327 c3_assert(0);
328 }
329 else if ( !out_u ) {
330 break;
331 }
332 else if ( '.' == out_u->d_name[0] ) { // unnecessary, but consistency
333 continue;
334 }
335 else if ( 0 != strncmp(mon_u->nam_c, out_u->d_name, len_w) ) {
336 continue;
337 }
338 else {
339 c3_c* pax_c = _unix_down(mon_u->dir_u.pax_c, out_u->d_name);
340
341 struct stat buf_u;
342
343 if ( 0 != stat(pax_c, &buf_u) ) {
344 uL(fprintf(uH, "can't stat pier directory %s: %s\r\n",
345 mon_u->dir_u.pax_c, strerror(errno)));
346 free(pax_c);
347 continue;
348 }
349 if ( S_ISDIR(buf_u.st_mode) ) {
350 if ( out_u->d_name[len_w] != '\0' ) {
351 free(pax_c);
352 continue;
353 }
354 else {
355 u3_udir* dir_u = c3_malloc(sizeof(u3_udir));
356 _unix_watch_dir(dir_u, &mon_u->dir_u, pax_c);
357 }
358 }
359 else {
360 if ( '.' != out_u->d_name[len_w]
361 || '\0' == out_u->d_name[len_w + 1]
362 || '~' == out_u->d_name[strlen(out_u->d_name) - 1]
363 || ('#' == out_u->d_name[0] &&
364 '#' == out_u->d_name[strlen(out_u->d_name) - 1])
365 ) {
366 free(pax_c);
367 continue;
368 }
369 else {
370 u3_ufil* fil_u = c3_malloc(sizeof(u3_ufil));
371 _unix_watch_file(fil_u, &mon_u->dir_u, pax_c);
372 }
373 }
374 }
375 }
376 }
377
378 static u3_noun _unix_free_node(u3_unod* nod_u);
379
380 /* _unix_free_file(): free file, unlinking it
381 */
382 static void
_unix_free_file(uv_handle_t * was_u)383 _unix_free_file(uv_handle_t* was_u)
384 {
385 u3_ufil* fil_u = (void*) was_u;
386
387 if ( 0 != unlink(fil_u->pax_c) && ENOENT != errno ) {
388 uL(fprintf(uH, "error unlinking %s: %s\n", fil_u->pax_c, strerror(errno)));
389 c3_assert(0);
390 }
391
392 free(fil_u->pax_c);
393 free(fil_u);
394 }
395
396 /* _unix_free_dir(): free directory, deleting everything within
397 */
398 static void
_unix_free_dir(uv_handle_t * was_u)399 _unix_free_dir(uv_handle_t* was_u)
400 {
401 u3_udir* dir_u = (void*) was_u;
402
403 _unix_rm_r(dir_u->pax_c);
404
405 if ( dir_u->kid_u ) {
406 fprintf(stderr, "don't kill me, i've got a family %s\r\n", dir_u->pax_c);
407 }
408 else {
409 // fprintf(stderr, "i'm a lone, lonely loner %s\r\n", dir_u->pax_c);
410 }
411 free(dir_u->pax_c);
412 free(dir_u); // XXX this might be too early, how do we
413 // know we've freed all the children?
414 // i suspect we should do this only if
415 // our kid list is empty
416 }
417
418 /* _unix_free_node(): free node, deleting everything within
419 *
420 * also deletes from parent list if in it
421 */
422 static u3_noun
_unix_free_node(u3_unod * nod_u)423 _unix_free_node(u3_unod* nod_u)
424 {
425 u3_noun can;
426 if ( nod_u->par_u ) {
427 u3_unod* don_u = nod_u->par_u->kid_u;
428
429 if ( !don_u ) {
430 }
431 else if ( nod_u == don_u ) {
432 nod_u->par_u->kid_u = nod_u->par_u->kid_u->nex_u;
433 }
434 else {
435 for ( ; don_u->nex_u && nod_u != don_u->nex_u; don_u = don_u->nex_u ) {
436 }
437 if ( don_u->nex_u ) {
438 don_u->nex_u = don_u->nex_u->nex_u;
439 }
440 }
441 }
442
443 if ( c3y == nod_u->dir ) {
444 can = u3_nul;
445 u3_unod* nud_u = ((u3_udir*) nod_u)->kid_u;
446 while ( nud_u ) {
447 u3_unod* nex_u = nud_u->nex_u;
448 can = u3kb_weld(_unix_free_node(nud_u), can);
449 nud_u = nex_u;
450 }
451
452 uv_close((uv_handle_t*)&nod_u->was_u, _unix_free_dir);
453 }
454 else {
455 can = u3nc(u3nc(_unix_string_to_path(nod_u->pax_c), u3_nul),
456 u3_nul);
457 uv_close((uv_handle_t*)&nod_u->was_u, _unix_free_file);
458 }
459
460 return can;
461 }
462
463 /* _unix_free_mount_point(): free mount point
464 *
465 * this process needs to happen in a very careful order. in particular,
466 * we must recurse before we get to the callback, so that libuv does all
467 * the child directories before it does us.
468 *
469 * tread carefully
470 */
471 static void
_unix_free_mount_point(u3_umon * mon_u)472 _unix_free_mount_point(u3_umon* mon_u)
473 {
474 u3_unod* nod_u;
475 for ( nod_u = mon_u->dir_u.kid_u; nod_u; ) {
476 u3_unod* nex_u = nod_u->nex_u;
477 u3z(_unix_free_node(nod_u));
478 nod_u = nex_u;
479 }
480
481 free(mon_u->dir_u.pax_c);
482 free(mon_u->nam_c);
483 free(mon_u);
484 }
485
486 /* _unix_delete_mount_point(): remove mount point from list and free
487 */
488 static void
_unix_delete_mount_point(u3_noun mon)489 _unix_delete_mount_point(u3_noun mon)
490 {
491 if ( c3n == u3ud(mon) ) {
492 c3_assert(!"mount point must be an atom");
493 u3z(mon);
494 return;
495 }
496
497 c3_c* nam_c = u3r_string(mon);
498 u3_umon* mon_u;
499 u3_umon* tem_u;
500
501 mon_u = u3_Host.unx_u.mon_u;
502 if ( !mon_u ) {
503 uL(fprintf(uH, "mount point already gone: %s\r\n", nam_c));
504 goto _delete_mount_point_out;
505 }
506 if ( 0 == strcmp(nam_c, mon_u->nam_c) ) {
507 u3_Host.unx_u.mon_u = mon_u->nex_u;
508 _unix_free_mount_point(mon_u);
509 goto _delete_mount_point_out;
510 }
511
512 for ( ;
513 mon_u->nex_u && 0 != strcmp(nam_c, mon_u->nex_u->nam_c);
514 mon_u = mon_u->nex_u )
515 {
516 }
517
518 if ( !mon_u->nex_u ) {
519 uL(fprintf(uH, "mount point already gone: %s\r\n", nam_c));
520 goto _delete_mount_point_out;
521 }
522
523 tem_u = mon_u->nex_u;
524 mon_u->nex_u = mon_u->nex_u->nex_u;
525 _unix_free_mount_point(tem_u);
526
527 _delete_mount_point_out:
528 free(nam_c);
529 u3z(mon);
530 }
531
532 /* _unix_time_cb: timer callback
533 */
534 static void
_unix_time_cb(uv_timer_t * tim_u)535 _unix_time_cb(uv_timer_t* tim_u)
536 {
537 u3_lo_open();
538 {
539 u3_Host.unx_u.alm = c3n;
540 u3_Host.unx_u.dyr = c3y;
541 }
542 u3_lo_shut(c3y);
543 }
544
545 /* _unix_fs_event_cb(): filesystem event callback.
546 */
547 static void
_unix_fs_event_cb(uv_fs_event_t * was_u,const c3_c * pax_c,c3_i evt_i,c3_i sas_i)548 _unix_fs_event_cb(uv_fs_event_t* was_u,
549 const c3_c* pax_c,
550 c3_i evt_i,
551 c3_i sas_i)
552 {
553 // note that we're doing something tricky and weird here.
554 //
555 // * libuv passes around a pointer to a uv_fs_event_t
556 // * we define a struct that STARTS with a uv_fs_event_t and then has
557 // more fields after it
558 // * this is what we pass into libuv up top
559 // * this is what we get out of libuv down below
560 // * thus a cast is cool
561 u3_unod* nod_u = (u3_unod*) was_u;
562
563 while ( nod_u ) {
564 nod_u->dry = c3n;
565 nod_u = (u3_unod*) nod_u->par_u;
566 }
567
568 // we start a timer so that in 100 ms we check the fs.
569 // the extra time is so that the fs "settles down".
570 // vim, for example, tends to delete and re-add files
571 // for safety purposes.
572 if ( c3y == u3_Host.unx_u.alm ) {
573 uv_timer_stop(&u3_Host.unx_u.tim_u);
574 }
575 else {
576 u3_Host.unx_u.alm = c3y;
577 }
578
579 u3_Host.unx_u.dyr = c3n;
580
581 uv_timer_start(&u3_Host.unx_u.tim_u, _unix_time_cb, 100, 0);
582 }
583
584 /* _unix_watch_file(): initialize file
585 */
586 static void
_unix_watch_file(u3_ufil * fil_u,u3_udir * par_u,c3_c * pax_c)587 _unix_watch_file(u3_ufil* fil_u, u3_udir* par_u, c3_c* pax_c)
588 {
589 // initialize fil_u
590
591 fil_u->dir = c3n;
592 fil_u->dry = c3n;
593 fil_u->pax_c = pax_c;
594 fil_u->par_u = par_u;
595 fil_u->nex_u = NULL;
596 fil_u->mug_w = 0;
597 fil_u->gum_w = 0;
598
599 if ( par_u ) {
600 fil_u->nex_u = par_u->kid_u;
601 par_u->kid_u = (u3_unod*) fil_u;
602 }
603
604 // stuff fil_u into libuv
605 // note that we're doing something tricky here
606 // see comment in _unix_fs_event_cb
607
608 c3_w ret_w = uv_fs_event_init(u3L, &fil_u->was_u);
609 if (0 != ret_w){
610 uL(fprintf(uH, "file event init: %s\n", uv_strerror(ret_w)));
611 c3_assert(0);
612 }
613
614 ret_w = uv_fs_event_start(&fil_u->was_u, _unix_fs_event_cb, pax_c, 0);
615 if ( 0 != ret_w ){
616 uL(fprintf(uH, "file event start %s: %s\n", fil_u->pax_c, uv_strerror(ret_w)));
617 c3_assert(0);
618 }
619 }
620
621 /* _unix_watch_dir(): initialize directory
622 */
623 static void
_unix_watch_dir(u3_udir * dir_u,u3_udir * par_u,c3_c * pax_c)624 _unix_watch_dir(u3_udir* dir_u, u3_udir* par_u, c3_c* pax_c)
625 {
626 // initialize dir_u
627
628 dir_u->dir = c3y;
629 dir_u->dry = c3n;
630 dir_u->pax_c = pax_c;
631 dir_u->par_u = par_u;
632 dir_u->nex_u = NULL;
633 dir_u->kid_u = NULL;
634
635 if ( par_u ) {
636 dir_u->nex_u = par_u->kid_u;
637 par_u->kid_u = (u3_unod*) dir_u;
638 }
639
640 // stuff dir_u into libuv
641 // note that we're doing something tricky here
642 // see comment in _unix_fs_event_cb
643
644 c3_w ret_w = uv_fs_event_init(u3L, &dir_u->was_u);
645 if (0 != ret_w){
646 uL(fprintf(uH, "directory event init: %s\n", uv_strerror(ret_w)));
647 c3_assert(0);
648 }
649
650 ret_w = uv_fs_event_start(&dir_u->was_u, _unix_fs_event_cb, pax_c, 0);
651 if (0 != ret_w){
652 uL(fprintf(uH, "directory event start: %s\n", uv_strerror(ret_w)));
653 c3_assert(0);
654 }
655 }
656
657 /* _unix_create_dir(): create unix directory and watch it
658 */
659 static void
_unix_create_dir(u3_udir * dir_u,u3_udir * par_u,u3_noun nam)660 _unix_create_dir(u3_udir* dir_u, u3_udir* par_u, u3_noun nam)
661 {
662 c3_c* nam_c = u3r_string(nam);
663 c3_w nam_w = strlen(nam_c);
664 c3_w pax_w = strlen(par_u->pax_c);
665 c3_c* pax_c = c3_malloc(pax_w + 1 + nam_w + 1);
666
667 strncpy(pax_c, par_u->pax_c, pax_w);
668 pax_c[pax_w] = '/';
669 strncpy(pax_c + pax_w + 1, nam_c, nam_w);
670 pax_c[pax_w + 1 + nam_w] = '\0';
671
672 free(nam_c);
673 u3z(nam);
674
675 _unix_mkdir(pax_c);
676 _unix_watch_dir(dir_u, par_u, pax_c);
677 }
678
679 static u3_noun _unix_update_node(u3_unod* nod_u);
680
681 /* _unix_update_file(): update file, producing list of changes
682 *
683 * when scanning through files, if dry, do nothing. otherwise, mark as
684 * dry, then check if file exists. if not, remove self from node list
685 * and add path plus sig to %into event. otherwise, read the file and
686 * get a mug checksum. if same as mug_w, move on. otherwise, overwrite
687 * mug_w with new mug and add path plus data to %into event.
688 */
689 static u3_noun
_unix_update_file(u3_ufil * fil_u)690 _unix_update_file(u3_ufil* fil_u)
691 {
692 c3_assert( c3n == fil_u->dir );
693
694 if ( c3y == fil_u->dry ) {
695 return u3_nul;
696 }
697
698 fil_u->dry = c3y;
699
700 struct stat buf_u;
701 c3_i fid_i = open(fil_u->pax_c, O_RDONLY, 0644);
702 c3_ws len_ws, red_ws;
703 c3_y* dat_y;
704
705 if ( fid_i < 0 || fstat(fid_i, &buf_u) < 0 ) {
706 if ( ENOENT == errno ) {
707 return u3nc(u3nc(_unix_string_to_path(fil_u->pax_c), u3_nul), u3_nul);
708 }
709 else {
710 uL(fprintf(uH, "error opening file %s: %s\r\n",
711 fil_u->pax_c, strerror(errno)));
712 return u3_nul;
713 }
714 }
715
716 // So, if file gets deleted and then quickly re-added, like vim and
717 // other editors do, we lose the notification. This is a bad thing,
718 // so we always stop and restart the notification.
719 uv_fs_event_stop(&fil_u->was_u);
720 c3_w ret_w = uv_fs_event_start(&fil_u->was_u,
721 _unix_fs_event_cb,
722 fil_u->pax_c,
723 0);
724 if ( 0 != ret_w ){
725 uL(fprintf(uH, "update file event start: %s\n", uv_strerror(ret_w)));
726 c3_assert(0);
727 }
728
729 len_ws = buf_u.st_size;
730 dat_y = c3_malloc(len_ws);
731
732 red_ws = read(fid_i, dat_y, len_ws);
733
734 if ( close(fid_i) < 0 ) {
735 uL(fprintf(uH, "error closing file %s: %s\r\n",
736 fil_u->pax_c, strerror(errno)));
737 }
738
739 if ( len_ws != red_ws ) {
740 if ( red_ws < 0 ) {
741 uL(fprintf(uH, "error reading file %s: %s\r\n",
742 fil_u->pax_c, strerror(errno)));
743 }
744 else {
745 uL(fprintf(uH, "wrong # of bytes read in file %s: %d %d\r\n",
746 fil_u->pax_c, len_ws, red_ws));
747 }
748 free(dat_y);
749 return u3_nul;
750 }
751 else {
752 c3_w mug_w = u3r_mug_bytes(dat_y, len_ws);
753 if ( mug_w == fil_u->mug_w ) {
754 free(dat_y);
755 return u3_nul;
756 }
757 else if ( mug_w == fil_u->gum_w ) {
758 fil_u->mug_w = mug_w;
759 free(dat_y);
760 return u3_nul;
761 }
762 else {
763 fil_u->mug_w = mug_w;
764
765 u3_noun pax = _unix_string_to_path(fil_u->pax_c);
766 u3_noun mim = u3nt(c3__text, u3i_string("plain"), u3_nul);
767 u3_noun dat = u3nt(mim, len_ws, u3i_bytes(len_ws, dat_y));
768
769 free(dat_y);
770 return u3nc(u3nt(pax, u3_nul, dat), u3_nul);
771 }
772 }
773 }
774
775 /* _unix_update_dir(): update directory, producing list of changes
776 *
777 * when changing this, consider whether to also change
778 * _unix_initial_update_dir()
779 */
780 static u3_noun
_unix_update_dir(u3_udir * dir_u)781 _unix_update_dir(u3_udir* dir_u)
782 {
783 u3_noun can = u3_nul;
784
785 c3_assert( c3y == dir_u->dir );
786
787 if ( c3y == dir_u->dry ) {
788 return u3_nul;
789 }
790
791 dir_u->dry = c3y;
792
793 // Check that old nodes are still there
794
795 u3_unod* nod_u = dir_u->kid_u;
796
797 if ( nod_u ) {
798 while ( nod_u ) {
799 if ( c3y == nod_u->dry ) {
800 nod_u = nod_u->nex_u;
801 }
802 else {
803 if ( c3y == nod_u->dir ) {
804 DIR* red_u = opendir(nod_u->pax_c);
805 if ( 0 == red_u ) {
806 u3_unod* nex_u = nod_u->nex_u;
807 can = u3kb_weld(_unix_free_node(nod_u), can);
808 nod_u = nex_u;
809 }
810 else {
811 closedir(red_u);
812 nod_u = nod_u->nex_u;
813 }
814 }
815 else {
816 struct stat buf_u;
817 c3_i fid_i = open(nod_u->pax_c, O_RDONLY, 0644);
818
819 if ( (fid_i < 0) || (fstat(fid_i, &buf_u) < 0) ) {
820 if ( ENOENT != errno ) {
821 uL(fprintf(uH, "_unix_update_dir: error opening file %s: %s\r\n",
822 nod_u->pax_c, strerror(errno)));
823 }
824
825 u3_unod* nex_u = nod_u->nex_u;
826 can = u3kb_weld(_unix_free_node(nod_u), can);
827 nod_u = nex_u;
828 }
829 else {
830 if ( close(fid_i) < 0 ) {
831 uL(fprintf(uH, "_unix_update_dir: error closing file %s: %s\r\n",
832 nod_u->pax_c, strerror(errno)));
833 }
834
835 nod_u = nod_u->nex_u;
836 }
837 }
838 }
839 }
840 }
841
842 // Check for new nodes
843
844 DIR* rid_u = opendir(dir_u->pax_c);
845 if ( !rid_u ) {
846 uL(fprintf(uH, "error opening directory %s: %s\r\n",
847 dir_u->pax_c, strerror(errno)));
848 c3_assert(0);
849 }
850
851 while ( 1 ) {
852 struct dirent ent_u;
853 struct dirent* out_u;
854 c3_w err_w;
855
856 if ( (err_w = readdir_r(rid_u, &ent_u, &out_u)) != 0 ) {
857 uL(fprintf(uH, "error loading directory %s: %s\r\n",
858 dir_u->pax_c, strerror(err_w)));
859 c3_assert(0);
860 }
861 else if ( !out_u ) {
862 break;
863 }
864 else if ( '.' == out_u->d_name[0] ) {
865 continue;
866 }
867 else {
868 c3_c* pax_c = _unix_down(dir_u->pax_c, out_u->d_name);
869
870 struct stat buf_u;
871
872 if ( 0 != stat(pax_c, &buf_u) ) {
873 uL(fprintf(uH, "can't stat %s: %s\r\n", pax_c, strerror(errno)));
874 free(pax_c);
875 continue;
876 }
877 else {
878 u3_unod* nod_u;
879 for ( nod_u = dir_u->kid_u; nod_u; nod_u = nod_u->nex_u ) {
880 if ( 0 == strcmp(pax_c, nod_u->pax_c) ) {
881 if ( S_ISDIR(buf_u.st_mode) ) {
882 if ( c3n == nod_u->dir ) {
883 uL(fprintf(uH, "not a directory: %s\r\n", nod_u->pax_c));
884 c3_assert(0);
885 }
886 }
887 else {
888 if ( c3y == nod_u->dir ) {
889 uL(fprintf(uH, "not a file: %s\r\n", nod_u->pax_c));
890 c3_assert(0);
891 }
892 }
893 break;
894 }
895 }
896
897 if ( !nod_u ) {
898 if ( !S_ISDIR(buf_u.st_mode) ) {
899 if ( !strchr(out_u->d_name,'.')
900 || '~' == out_u->d_name[strlen(out_u->d_name) - 1]
901 || ('#' == out_u->d_name[0] &&
902 '#' == out_u->d_name[strlen(out_u->d_name) - 1])
903 ) {
904 free(pax_c);
905 continue;
906 }
907
908 u3_ufil* fil_u = c3_malloc(sizeof(u3_ufil));
909 _unix_watch_file(fil_u, dir_u, pax_c);
910 }
911 else {
912 u3_udir* dis_u = c3_malloc(sizeof(u3_udir));
913 _unix_watch_dir(dis_u, dir_u, pax_c);
914 can = u3kb_weld(_unix_update_dir(dis_u), can); // XXX unnecessary?
915 }
916 }
917 }
918 }
919 }
920
921 if ( closedir(rid_u) < 0 ) {
922 uL(fprintf(uH, "error closing directory %s: %s\r\n",
923 dir_u->pax_c, strerror(errno)));
924 }
925
926 if ( !dir_u->kid_u ) {
927 return u3kb_weld(_unix_free_node((u3_unod*) dir_u), can);
928 }
929
930 // get change list
931
932 for ( nod_u = dir_u->kid_u; nod_u; nod_u = nod_u->nex_u ) {
933 can = u3kb_weld(_unix_update_node(nod_u), can);
934 }
935
936 return can;
937 }
938
939 /* _unix_update_node(): update node, producing list of changes
940 */
941 static u3_noun
_unix_update_node(u3_unod * nod_u)942 _unix_update_node(u3_unod* nod_u)
943 {
944 if ( c3y == nod_u->dir ) {
945 return _unix_update_dir((void*)nod_u);
946 }
947 else {
948 return _unix_update_file((void*)nod_u);
949 }
950 }
951
952 /* _unix_update_mount(): update mount point
953 */
954 static void
_unix_update_mount(u3_umon * mon_u,u3_noun all)955 _unix_update_mount(u3_umon* mon_u, u3_noun all)
956 {
957 if ( c3n == mon_u->dir_u.dry ) {
958 u3_noun can = u3_nul;
959 u3_unod* nod_u;
960 for ( nod_u = mon_u->dir_u.kid_u; nod_u; nod_u = nod_u->nex_u ) {
961 can = u3kb_weld(_unix_update_node(nod_u), can);
962 }
963
964 u3v_plan(u3nq(u3_blip, c3__sync, u3k(u3A->sen), u3_nul),
965 u3nq(c3__into, u3i_string(mon_u->nam_c), all, can));
966 }
967 }
968
969 /* _unix_initial_update_file(): read file, but don't watch
970 */
971 static u3_noun
_unix_initial_update_file(c3_c * pax_c)972 _unix_initial_update_file(c3_c* pax_c)
973 {
974 struct stat buf_u;
975 c3_i fid_i = open(pax_c, O_RDONLY, 0644);
976 c3_ws len_ws, red_ws;
977 c3_y* dat_y;
978
979 if ( fid_i < 0 || fstat(fid_i, &buf_u) < 0 ) {
980 if ( ENOENT == errno ) {
981 return u3_nul;
982 }
983 else {
984 uL(fprintf(uH, "error opening initial file %s: %s\r\n",
985 pax_c, strerror(errno)));
986 return u3_nul;
987 }
988 }
989
990 len_ws = buf_u.st_size;
991 dat_y = c3_malloc(len_ws);
992
993 red_ws = read(fid_i, dat_y, len_ws);
994
995 if ( close(fid_i) < 0 ) {
996 uL(fprintf(uH, "error closing initial file %s: %s\r\n",
997 pax_c, strerror(errno)));
998 }
999
1000 if ( len_ws != red_ws ) {
1001 if ( red_ws < 0 ) {
1002 uL(fprintf(uH, "error reading initial file %s: %s\r\n",
1003 pax_c, strerror(errno)));
1004 }
1005 else {
1006 uL(fprintf(uH, "wrong # of bytes read in initial file %s: %d %d\r\n",
1007 pax_c, len_ws, red_ws));
1008 }
1009 free(dat_y);
1010 return u3_nul;
1011 }
1012 else {
1013 u3_noun pax = _unix_string_to_path_helper(pax_c
1014 + strlen(u3_Host.ops_u.arv_c)
1015 + 1); /* XX slightly less VERY BAD than before*/
1016 u3_noun mim = u3nt(c3__text, u3i_string("plain"), u3_nul);
1017 u3_noun dat = u3nt(mim, len_ws, u3i_bytes(len_ws, dat_y));
1018
1019 free(dat_y);
1020 return u3nc(u3nt(pax, u3_nul, dat), u3_nul);
1021 }
1022 }
1023
1024 /* _unix_initial_update_dir(): read directory, but don't watch
1025 */
1026 static u3_noun
_unix_initial_update_dir(c3_c * pax_c)1027 _unix_initial_update_dir(c3_c* pax_c)
1028 {
1029 u3_noun can = u3_nul;
1030
1031 DIR* rid_u = opendir(pax_c);
1032 if ( !rid_u ) {
1033 uL(fprintf(uH, "error opening initial directory: %s: %s\r\n",
1034 pax_c, strerror(errno)));
1035 return u3_nul;
1036 }
1037
1038 while ( 1 ) {
1039 struct dirent ent_u;
1040 struct dirent* out_u;
1041 c3_w err_w;
1042
1043 if ( 0 != (err_w = readdir_r(rid_u, &ent_u, &out_u)) ) {
1044 uL(fprintf(uH, "error loading initial directory %s: %s\r\n",
1045 pax_c, strerror(errno)));
1046 c3_assert(0);
1047 }
1048 else if ( !out_u ) {
1049 break;
1050 }
1051 else if ( '.' == out_u->d_name[0] ) {
1052 continue;
1053 }
1054 else {
1055 c3_c* pox_c = _unix_down(pax_c, out_u->d_name);
1056
1057 struct stat buf_u;
1058
1059 if ( 0 != stat(pox_c, &buf_u) ) {
1060 uL(fprintf(uH, "initial can't stat %s: %s\r\n",
1061 pox_c, strerror(errno)));
1062 free(pox_c);
1063 continue;
1064 }
1065 else {
1066 if ( S_ISDIR(buf_u.st_mode) ) {
1067 can = u3kb_weld(_unix_initial_update_dir(pox_c), can);
1068 }
1069 else {
1070 can = u3kb_weld(_unix_initial_update_file(pox_c), can);
1071 }
1072 free(pox_c);
1073 }
1074 }
1075 }
1076
1077 if ( closedir(rid_u) < 0 ) {
1078 uL(fprintf(uH, "error closing initial directory %s: %s\r\n",
1079 pax_c, strerror(errno)));
1080 }
1081
1082 return can;
1083 }
1084
1085 /* _unix_sign_cb: signal callback.
1086 */
1087 static void
_unix_sign_cb(uv_signal_t * sil_u,c3_i num_i)1088 _unix_sign_cb(uv_signal_t* sil_u, c3_i num_i)
1089 {
1090 u3_lo_open();
1091 {
1092 switch ( num_i ) {
1093 default: fprintf(stderr, "\r\nmysterious signal %d\r\n", num_i); break;
1094 case SIGTERM:
1095 fprintf(stderr, "\r\ncaught signal %d\r\n", num_i);
1096 u3_Host.liv = c3n;
1097 break;
1098 case SIGINT:
1099 fprintf(stderr, "\r\ninterrupt\r\n");
1100 u3_term_ef_ctlc();
1101 break;
1102 case SIGWINCH: u3_term_ef_winc(); break;
1103 }
1104 }
1105 u3_lo_shut(c3y);
1106 }
1107
1108 /* _unix_ef_sync(): check for files to sync.
1109 */
1110 static void
_unix_ef_sync(uv_check_t * han_u)1111 _unix_ef_sync(uv_check_t* han_u)
1112 {
1113 u3_lo_open();
1114 u3_lo_shut(c3y);
1115 }
1116
1117 /* _unix_sync_file(): sync file to unix
1118 */
1119 static void
_unix_sync_file(u3_udir * par_u,u3_noun nam,u3_noun ext,u3_noun mim)1120 _unix_sync_file(u3_udir* par_u, u3_noun nam, u3_noun ext, u3_noun mim)
1121 {
1122 c3_assert( par_u );
1123 c3_assert( c3y == par_u->dir );
1124
1125 // form file path
1126
1127 c3_c* nam_c = u3r_string(nam);
1128 c3_c* ext_c = u3r_string(ext);
1129 c3_w par_w = strlen(par_u->pax_c);
1130 c3_w nam_w = strlen(nam_c);
1131 c3_w ext_w = strlen(ext_c);
1132 c3_c* pax_c = c3_malloc(par_w + 1 + nam_w + 1 + ext_w + 1);
1133
1134 strncpy(pax_c, par_u->pax_c, par_w);
1135 pax_c[par_w] = '/';
1136 strncpy(pax_c + par_w + 1, nam_c, nam_w);
1137 pax_c[par_w + 1 + nam_w] = '.';
1138 strncpy(pax_c + par_w + 1 + nam_w + 1, ext_c, ext_w);
1139 pax_c[par_w + 1 + nam_w + 1 + ext_w] = '\0';
1140
1141 free(nam_c); free(ext_c);
1142 u3z(nam); u3z(ext);
1143
1144 // check whether we already know about this file
1145
1146 u3_unod* nod_u;
1147 for ( nod_u = par_u->kid_u;
1148 ( nod_u &&
1149 ( c3y == nod_u->dir ||
1150 0 != strcmp(nod_u->pax_c, pax_c) ) );
1151 nod_u = nod_u->nex_u )
1152 { }
1153
1154 // apply change
1155
1156 if ( u3_nul == mim ) {
1157 if ( nod_u ) {
1158 u3z(_unix_free_node(nod_u));
1159 }
1160 }
1161 else {
1162
1163 if ( !nod_u ) {
1164 c3_w gum_w = _unix_write_file_hard(pax_c, u3k(u3t(mim)));
1165 u3_ufil* fil_u = c3_malloc(sizeof(u3_ufil));
1166 _unix_watch_file(fil_u, par_u, pax_c);
1167 fil_u->gum_w = gum_w;
1168 goto _unix_sync_file_out;
1169 }
1170 else {
1171 _unix_write_file_soft((u3_ufil*) nod_u, u3k(u3t(mim)));
1172 }
1173 }
1174
1175 free(pax_c);
1176
1177 _unix_sync_file_out:
1178 u3z(mim);
1179 }
1180
1181 /* _unix_sync_change(): sync single change to unix
1182 */
1183 static void
_unix_sync_change(u3_udir * dir_u,u3_noun pax,u3_noun mim)1184 _unix_sync_change(u3_udir* dir_u, u3_noun pax, u3_noun mim)
1185 {
1186 c3_assert( c3y == dir_u->dir );
1187
1188 if ( c3n == u3du(pax) ) {
1189 if ( u3_nul == pax ) {
1190 uL(fprintf(uH,"can't sync out file as top-level, strange\r\n"));
1191 }
1192 else {
1193 uL(fprintf(uH,"sync out: bad path\r\n"));
1194 }
1195 u3z(pax); u3z(mim);
1196 return;
1197 }
1198 else if ( c3n == u3du(u3t(pax)) ) {
1199 uL(fprintf(uH,"can't sync out file as top-level, strangely\r\n"));
1200 u3z(pax); u3z(mim);
1201 }
1202 else {
1203 u3_noun i_pax = u3h(pax);
1204 u3_noun t_pax = u3t(pax);
1205 u3_noun it_pax = u3h(t_pax);
1206 u3_noun tt_pax = u3t(t_pax);
1207
1208 if ( u3_nul == tt_pax ) {
1209 _unix_sync_file(dir_u, u3k(i_pax), u3k(it_pax), mim);
1210 }
1211 else {
1212 c3_c* nam_c = u3r_string(i_pax);
1213 c3_w pax_w = strlen(dir_u->pax_c);
1214 u3_unod* nod_u;
1215
1216 for ( nod_u = dir_u->kid_u;
1217 ( nod_u &&
1218 ( c3n == nod_u->dir ||
1219 0 != strcmp(nod_u->pax_c + pax_w + 1, nam_c) ) );
1220 nod_u = nod_u->nex_u )
1221 { }
1222
1223 if ( !nod_u ) {
1224 nod_u = c3_malloc(sizeof(u3_udir));
1225 _unix_create_dir((u3_udir*) nod_u, dir_u, u3k(i_pax));
1226 }
1227
1228 if ( c3n == nod_u->dir ) {
1229 uL(fprintf(uH,
1230 "weird, we got a file when we weren't expecting to\r\n"));
1231 c3_assert(0);
1232 }
1233
1234 _unix_sync_change((u3_udir*) nod_u, u3k(t_pax), mim);
1235 }
1236 }
1237 u3z(pax);
1238 }
1239
1240 /* _unix_sync_ergo(): sync list of changes to unix
1241 */
1242 static void
_unix_sync_ergo(u3_umon * mon_u,u3_noun can)1243 _unix_sync_ergo(u3_umon* mon_u, u3_noun can)
1244 {
1245 u3_noun nac = can;
1246 u3_noun nam = u3i_string(mon_u->nam_c);
1247
1248 while ( u3_nul != nac) {
1249 _unix_sync_change(&mon_u->dir_u,
1250 u3nc(u3k(nam), u3k(u3h(u3h(nac)))),
1251 u3k(u3t(u3h(nac))));
1252 nac = u3t(nac);
1253 }
1254
1255 u3z(nam);
1256 u3z(can);
1257 }
1258
1259 /* u3_unix_ef_ergo(): update filesystem from urbit
1260 */
1261 void
u3_unix_ef_ergo(u3_noun mon,u3_noun can)1262 u3_unix_ef_ergo(u3_noun mon, u3_noun can)
1263 {
1264 u3_umon* mon_u = _unix_get_mount_point(mon);
1265
1266 _unix_sync_ergo(mon_u, can);
1267 }
1268
1269 /* u3_unix_ef_ogre(): delete mount point
1270 */
1271 void
u3_unix_ef_ogre(u3_noun mon)1272 u3_unix_ef_ogre(u3_noun mon)
1273 {
1274 _unix_delete_mount_point(mon);
1275 }
1276
1277 /* u3_unix_ef_hill(): enumerate mount points
1278 */
1279 void
u3_unix_ef_hill(u3_noun hil)1280 u3_unix_ef_hill(u3_noun hil)
1281 {
1282 u3_noun mon;
1283 for ( mon = hil; c3y == u3du(mon); mon = u3t(mon) ) {
1284 u3_umon* mon_u = _unix_get_mount_point(u3k(u3h(mon)));
1285 _unix_scan_mount_point(mon_u);
1286 }
1287 u3z(hil);
1288 u3_Host.unx_u.dyr = c3y;
1289 u3_unix_ef_look(c3y);
1290 }
1291
1292 /* u3_unix_io_init(): initialize unix sync.
1293 */
1294 void
u3_unix_io_init(void)1295 u3_unix_io_init(void)
1296 {
1297 u3_unix* unx_u = &u3_Host.unx_u;
1298
1299 unx_u->mon_u = NULL;
1300
1301 {
1302 u3_usig* sig_u;
1303
1304 sig_u = c3_malloc(sizeof(u3_usig));
1305 uv_signal_init(u3L, &sig_u->sil_u);
1306
1307 sig_u->num_i = SIGTERM;
1308 sig_u->nex_u = unx_u->sig_u;
1309 unx_u->sig_u = sig_u;
1310 }
1311 {
1312 u3_usig* sig_u;
1313
1314 sig_u = c3_malloc(sizeof(u3_usig));
1315 uv_signal_init(u3L, &sig_u->sil_u);
1316
1317 sig_u->num_i = SIGINT;
1318 sig_u->nex_u = unx_u->sig_u;
1319 unx_u->sig_u = sig_u;
1320 }
1321 {
1322 u3_usig* sig_u;
1323
1324 sig_u = c3_malloc(sizeof(u3_usig));
1325 uv_signal_init(u3L, &sig_u->sil_u);
1326
1327 sig_u->num_i = SIGWINCH;
1328 sig_u->nex_u = unx_u->sig_u;
1329 unx_u->sig_u = sig_u;
1330 }
1331
1332 uv_check_init(u3_Host.lup_u, &u3_Host.unx_u.syn_u);
1333
1334 uv_timer_init(u3L, &unx_u->tim_u);
1335 unx_u->alm = c3n;
1336 unx_u->dyr = c3n;
1337
1338 if ( c3n == u3_Host.ops_u.nuu ) {
1339 u3v_plan(u3nt(u3_blip, c3__boat, u3_nul),
1340 u3nc(c3__boat, u3_nul));
1341 }
1342 }
1343
1344 /* u3_unix_acquire(): acquire a lockfile, killing anything that holds it.
1345 */
1346 static void
u3_unix_acquire(c3_c * pax_c)1347 u3_unix_acquire(c3_c* pax_c)
1348 {
1349 c3_c* paf_c = _unix_down(pax_c, ".vere.lock");
1350 c3_w pid_w;
1351 FILE* loq_u;
1352
1353 if ( NULL != (loq_u = fopen(paf_c, "r")) ) {
1354 if ( 1 != fscanf(loq_u, "%" SCNu32, &pid_w) ) {
1355 uL(fprintf(uH, "lockfile %s is corrupt!\n", paf_c));
1356 kill(getpid(), SIGTERM);
1357 sleep(1); c3_assert(0);
1358 }
1359 else if (pid_w != getpid()) {
1360 c3_w i_w;
1361
1362 if ( -1 != kill(pid_w, SIGTERM) ) {
1363 uL(fprintf(uH, "unix: stopping process %d, live in %s...\n",
1364 pid_w, pax_c));
1365
1366 for ( i_w = 0; i_w < 16; i_w++ ) {
1367 sleep(1);
1368 if ( -1 == kill(pid_w, SIGTERM) ) {
1369 break;
1370 }
1371 }
1372 if ( 16 == i_w ) {
1373 for ( i_w = 0; i_w < 16; i_w++ ) {
1374 if ( -1 == kill(pid_w, SIGKILL) ) {
1375 break;
1376 }
1377 sleep(1);
1378 }
1379 }
1380 if ( 16 == i_w ) {
1381 uL(fprintf(uH, "process %d seems unkillable!\n", pid_w));
1382 c3_assert(0);
1383 }
1384 uL(fprintf(uH, "unix: stopped old process %u\n", pid_w));
1385 }
1386 }
1387 fclose(loq_u);
1388 unlink(paf_c);
1389 }
1390
1391 loq_u = fopen(paf_c, "w");
1392 fprintf(loq_u, "%u\n", getpid());
1393
1394 {
1395 c3_i fid_i = fileno(loq_u);
1396 #if defined(U3_OS_linux)
1397 fdatasync(fid_i);
1398 #elif defined(U3_OS_osx)
1399 fcntl(fid_i, F_FULLFSYNC);
1400 #elif defined(U3_OS_bsd)
1401 fsync(fid_i);
1402 #else
1403 # error "port: datasync"
1404 #endif
1405 }
1406 fclose(loq_u);
1407 free(paf_c);
1408 }
1409
1410 /* u3_unix_release(): release a lockfile.
1411 */
1412 static void
u3_unix_release(c3_c * pax_c)1413 u3_unix_release(c3_c* pax_c)
1414 {
1415 c3_c* paf_c = _unix_down(pax_c, ".vere.lock");
1416
1417 unlink(paf_c);
1418 free(paf_c);
1419 }
1420
1421 /* u3_unix_ef_hold()
1422 */
1423 void
u3_unix_ef_hold(void)1424 u3_unix_ef_hold(void)
1425 {
1426 u3_unix* unx_u = &u3_Host.unx_u;
1427 u3_usig* sig_u;
1428
1429 for ( sig_u = unx_u->sig_u; sig_u; sig_u = sig_u->nex_u ) {
1430 uv_signal_stop(&sig_u->sil_u);
1431 }
1432 }
1433
1434 /* u3_unix_ef_move()
1435 */
1436 void
u3_unix_ef_move(void)1437 u3_unix_ef_move(void)
1438 {
1439 u3_unix* unx_u = &u3_Host.unx_u;
1440 u3_usig* sig_u;
1441
1442 for ( sig_u = unx_u->sig_u; sig_u; sig_u = sig_u->nex_u ) {
1443 uv_signal_start(&sig_u->sil_u, _unix_sign_cb, sig_u->num_i);
1444 }
1445 }
1446
1447 void
u3_unix_ef_initial_into()1448 u3_unix_ef_initial_into()
1449 {
1450 u3_noun can = _unix_initial_update_dir(u3_Host.ops_u.arv_c);
1451
1452 u3v_plan(u3nq(u3_blip, c3__sync, u3k(u3A->sen), u3_nul),
1453 u3nq(c3__into, u3_nul, c3y, can));
1454 }
1455
1456 /* u3_unix_ef_look(): update the root.
1457 */
1458 void
u3_unix_ef_look(u3_noun all)1459 u3_unix_ef_look(u3_noun all)
1460 {
1461 if ( c3y == u3_Host.unx_u.dyr ) {
1462 u3_Host.unx_u.dyr = c3n;
1463 u3_umon* mon_u;
1464
1465 for ( mon_u = u3_Host.unx_u.mon_u; mon_u; mon_u = mon_u->nex_u ) {
1466 _unix_update_mount(mon_u, all);
1467 }
1468 }
1469 }
1470
1471 /* u3_unix_io_talk(): start listening for fs events.
1472 */
1473 void
u3_unix_io_talk()1474 u3_unix_io_talk()
1475 {
1476 u3_unix_acquire(u3_Host.dir_c);
1477 u3_unix_ef_move();
1478 uv_check_start(&u3_Host.unx_u.syn_u, _unix_ef_sync);
1479 }
1480
1481 /* u3_unix_io_exit(): terminate unix I/O.
1482 */
1483 void
u3_unix_io_exit(void)1484 u3_unix_io_exit(void)
1485 {
1486 uv_check_stop(&u3_Host.unx_u.syn_u);
1487 u3_unix_release(u3_Host.dir_c);
1488 }
1489
1490 /* u3_unix_io_poll(): update unix IO state.
1491 */
1492 void
u3_unix_io_poll(void)1493 u3_unix_io_poll(void)
1494 {
1495 }
1496