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