1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1991-1995, 1997-1999, 2001-2008, 2011, 2012 Peter Miller
4 // Copyright (C) 2007-2009 Walter Franzini
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see
18 // <http://www.gnu.org/licenses/>.
19 //
20
21 #include <common/ac/assert.h>
22 #include <common/ac/ctype.h>
23 #include <common/ac/stdio.h>
24 #include <common/ac/stdlib.h>
25 #include <common/ac/string.h>
26 #include <common/ac/unistd.h>
27
28 #include <common/env.h>
29 #include <common/error.h>
30 #include <common/libdir.h>
31 #include <common/mem.h>
32 #include <common/nstring/list.h>
33 #include <common/progname.h>
34 #include <common/str_list.h>
35 #include <common/trace.h>
36 #include <libaegis/arglex2.h>
37 #include <libaegis/commit.h>
38 #include <libaegis/gonzo.h>
39 #include <libaegis/gstate.fmtgen.h>
40 #include <libaegis/lock.h>
41 #include <libaegis/os.h>
42 #include <libaegis/project.h>
43 #include <libaegis/sub.h>
44 #include <libaegis/undo.h>
45 #include <libaegis/user.h>
46 #include <libaegis/ustate.fmtgen.h>
47
48 struct gonzo_ty
49 {
50 string_ty *dir;
51 string_ty *gstate_filename;
52 gstate_ty *gstate_data;
53 int is_a_new_file;
54 int modified;
55 long lock_magic;
56 int temporary;
57 };
58
59 static size_t ngonzos;
60 static size_t ngonzos_max;
61 static gonzo_ty **gonzo;
62 static int done_tail;
63
64
65 static user_ty::pointer
gonzo_user(void)66 gonzo_user(void)
67 {
68 static user_ty::pointer u;
69
70 if (!u)
71 {
72 u = user_ty::create(configured_aegis_uid(), configured_aegis_gid());
73 u->umask_set(022);
74
75 if (u->get_uid() >= AEGIS_MIN_UID)
76 {
77 sub_context_ty *scp;
78
79 scp = sub_context_new();
80 sub_var_set_long(scp, "Number1", u->get_uid());
81 sub_var_set_long(scp, "Number2", AEGIS_MIN_UID);
82 fatal_intl
83 (
84 scp,
85 i18n("AEGIS_USER_UID ($number1) vs AEGIS_MIN_UID ($number2) misconfigured")
86 );
87 // NOTREACHED
88 sub_context_delete(scp);
89 }
90 if (u->get_gid() >= AEGIS_MIN_GID)
91 {
92 sub_context_ty *scp;
93
94 scp = sub_context_new();
95 sub_var_set_long(scp, "Number1", u->get_gid());
96 sub_var_set_long(scp, "Number2", AEGIS_MIN_GID);
97 fatal_intl
98 (
99 scp,
100 i18n("AEGIS_USER_GID ($number1) vs AEGIS_MIN_GID ($number2) misconfigured")
101 );
102 // NOTREACHED
103 sub_context_delete(scp);
104 }
105 }
106 return u;
107 }
108
109
110 static int
is_temporary(string_ty * s)111 is_temporary(string_ty *s)
112 {
113 return !!strstr(s->str_text, "/tmp/");
114 }
115
116
117 void
gonzo_library_append(const char * s)118 gonzo_library_append(const char *s)
119 {
120 gonzo_ty *gp;
121 string_ty *tmp;
122 string_ty *dir;
123
124 //
125 // resolve the pathname
126 //
127 trace(("gonzo_library_append(s = \"%s\")\n{\n", s));
128 assert(s);
129 assert(!done_tail);
130 tmp = str_from_c(s);
131 os_become_orig();
132 dir = os_pathname(tmp, 1);
133 os_become_undo();
134 str_free(tmp);
135
136 //
137 // append the new entry to the end of the list
138 //
139 gp = (gonzo_ty *)mem_alloc_clear(sizeof(gonzo_ty));
140 gp->dir = dir;
141 gp->temporary = is_temporary(dir);
142 gp->gstate_filename = str_format("%s/state", gp->dir->str_text);
143 trace(("gonzo = %p;\n", gonzo));
144 trace(("ngonzos = %ld;\n", (long)ngonzos));
145
146 if (ngonzos >= ngonzos_max)
147 {
148 ngonzos_max = ngonzos_max * 2 + 4;
149 gonzo_ty **new_gonzo = new gonzo_ty * [ngonzos_max];
150 for (size_t k = 0; k < ngonzos; ++k)
151 new_gonzo[k] = gonzo[k];
152 delete [] gonzo;
153 gonzo = new_gonzo;
154 }
155 gonzo[ngonzos++] = gp;
156 trace(("}\n"));
157 }
158
159
160 static void
lock_sync(gonzo_ty * gp)161 lock_sync(gonzo_ty *gp)
162 {
163 trace(("lock_sync(gp = %p)\n{\n", gp));
164 long n = lock_magic();
165 if (gp->lock_magic == n)
166 goto ret;
167 assert(!gp->modified);
168 gp->lock_magic = n;
169
170 if (gp->gstate_data && !gp->is_a_new_file)
171 {
172 gstate_type.free(gp->gstate_data);
173 gp->gstate_data = 0;
174 }
175 ret:
176 trace(("}\n"));
177 }
178
179
180 static gstate_ty *
gonzo_gstate_get(gonzo_ty * gp)181 gonzo_gstate_get(gonzo_ty *gp)
182 {
183 trace(("gonzo_gstate_get(gp = %p)\n{\n", gp));
184 lock_sync(gp);
185 if (!gp->gstate_data)
186 {
187 //
188 // The problem here is if we get EACCES (Permission denied)
189 // for the lstat to see if the file exists. For now, we say
190 // that EACCESS is OK (that's what the second argument says)
191 // because most of the time this means we simply ignore global
192 // state files we can't read. But if there is a new project
193 // being created (the only time one of these ignored files will
194 // need writing) when the "new" file is opened, the EACCES will
195 // happen again, so nothing will be damaged, and the user will
196 // see an appropriate error.
197 //
198 gonzo_become();
199 gp->is_a_new_file = !os_exists(gp->gstate_filename, true);
200 if (!gp->is_a_new_file)
201 {
202 os_chown_check
203 (
204 gp->gstate_filename,
205 0644,
206 gonzo_user()->get_uid(),
207 (gp->temporary ? -1 : gonzo_user()->get_gid())
208 );
209 gp->gstate_data = gstate_read_file(gp->gstate_filename);
210 }
211 else
212 gp->gstate_data = (gstate_ty *)gstate_type.alloc();
213 if (!gp->gstate_data->where)
214 gp->gstate_data->where =
215 (gstate_where_list_ty *)gstate_where_list_type.alloc();
216 gonzo_become_undo();
217 }
218 trace(("return %p;\n", gp->gstate_data));
219 trace(("}\n"));
220 return gp->gstate_data;
221 }
222
223
224 //
225 // NAME
226 // construct_library_directory
227 //
228 // SYNOPSIS
229 // void construct_library_directory(gonzo_ty *gp);
230 //
231 // DESCRIPTION
232 // The construct_library_directory function is used to
233 // construct missing elements of the aegis library search path.
234 // These elements must be constructed with great care so that
235 // they may work on NFS mounted disks of "data-less" clients.
236 //
237 // The path up to, but not including, the library directory must
238 // be owned by the executing user. The actions will fail if the
239 // executing user does not have sufficient permissions, as
240 // one would expect.
241 //
242 // The library directory itself must be owned by AEGIS_USER.
243 // This is to defend against tampering and ignorance.
244 // Having this change of ownership requires some fast footwork
245 // with chmod's to allow AEGIS_USER temporary access.
246 //
247 // ARGUMENTS
248 // gp - library directory to act upon
249 //
250 // CAVEAT
251 // The chown() system call will not work on data-less clients,
252 // because the remote hosts which owns the NFS mounted disks
253 // will rarely trust the local "root" user, and map all
254 // foreign "root" users to the "unknown" user instead.
255 //
256
257 static void
construct_library_directory(gonzo_ty * gp)258 construct_library_directory(gonzo_ty *gp)
259 {
260 int exists;
261 string_ty *above;
262 string_ty *above2;
263 string_ty *root;
264 int mode;
265
266 //
267 // If the library directory already exists,
268 // then we need do nothing.
269 //
270 os_become_orig();
271 exists = os_exists(gp->dir);
272 if (exists)
273 {
274 os_become_undo();
275 return;
276 }
277
278 //
279 // Construct all directories up to,
280 // but not including, the library directory,
281 // if they do not already exist.
282 // Construct these directories as the original user,
283 // possibly discovering relevant permission problems.
284 //
285 above = os_dirname(gp->dir);
286 above2 = str_n_from_c(above->str_text + 1, above->str_length - 1);
287 root = str_from_c("/");
288 os_mkdir_between(root, above2, 0755); // NOT setgid
289 str_free(root);
290 str_free(above2);
291
292 //
293 // Get the mode of the directory containing the library directory,
294 // so that we may restore it later. Change the mode to world
295 // writable, so that the library directory itself may be created
296 // belonging to gonzo.
297 //
298 mode = os_chmod_query(above);
299 mode |= 0111; // minimum: all may search
300 undo_chmod_errok(above, mode);
301 os_chmod(above, mode | 0777);
302 os_become_undo();
303
304 //
305 // Create the library directory itself belonging to gonzo,
306 // and make sure it is world accessable.
307 // (must be world writable in testing situations)
308 //
309 gonzo_become();
310 os_mkdir(gp->dir, 0755);
311 if (gp->temporary)
312 os_chmod(gp->dir, 0777);
313 gonzo_become_undo();
314
315 //
316 // Restore permissions for the containing directory.
317 //
318 os_become_orig();
319 os_chmod(above, mode);
320 os_become_undo();
321 str_free(above);
322 }
323
324
325 static void
gonzo_gstate_write_sub(gonzo_ty * gp)326 gonzo_gstate_write_sub(gonzo_ty *gp)
327 {
328 string_ty *filename_new;
329 string_ty *filename_old;
330 static int count;
331
332 if (!gp->modified)
333 return;
334 trace(("gonzo_gstate_write_sub()\n{\n"));
335 assert(gp->gstate_data);
336 assert(gp->gstate_filename);
337 filename_new = str_format("%s,%d", gp->gstate_filename->str_text, ++count);
338 filename_old = str_format("%s,%d", gp->gstate_filename->str_text, ++count);
339 if (gp->is_a_new_file)
340 {
341 construct_library_directory(gp);
342 gonzo_become();
343 undo_unlink_errok(filename_new);
344 gstate_write_file(filename_new, gp->gstate_data, 0);
345 commit_rename(filename_new, gp->gstate_filename);
346 os_chmod(filename_new, 0644);
347 gonzo_become_undo();
348 }
349 else
350 {
351 gonzo_become();
352 undo_unlink_errok(filename_new);
353 gstate_write_file(filename_new, gp->gstate_data, 0);
354 commit_rename(gp->gstate_filename, filename_old);
355 commit_rename(filename_new, gp->gstate_filename);
356 commit_unlink_errok(filename_old);
357 os_chmod(filename_new, 0644);
358 gonzo_become_undo();
359 }
360 str_free(filename_new);
361 str_free(filename_old);
362 gp->modified = 0;
363 trace(("}\n"));
364 }
365
366
367 static void
do_tail(void)368 do_tail(void)
369 {
370 string_ty *s1;
371 string_ty *s2;
372 char *cp;
373 size_t j;
374 size_t max;
375
376 //
377 // only do this once
378 //
379 if (done_tail)
380 return;
381
382 //
383 // fetch the environment variable
384 //
385 trace(("do_tail()\n{\n"));
386 cp = getenv("AEGIS_PATH");
387 if (!cp)
388 {
389 cp = getenv("AEGIS");
390 if (cp)
391 {
392 sub_context_ty *scp;
393
394 scp = sub_context_new();
395 sub_var_set_charstar(scp, "Name1", "AEGIS");
396 sub_var_set_charstar(scp, "Name2", "AEGIS_PATH");
397 verbose_intl
398 (
399 scp,
400 i18n("warning: $name1 is obsolete, use $name2 environment variable")
401 );
402 sub_context_delete(scp);
403 }
404 }
405 if (cp)
406 {
407 s1 = str_from_c(cp);
408 string_list_ty path;
409 path.split(s1, ":", true);
410 str_free(s1);
411 for (j = 0; j < path.nstrings; ++j)
412 {
413 s1 = path.string[j];
414 if (!os_testing_mode() || is_temporary(s1))
415 gonzo_library_append(s1->str_text);
416 }
417 }
418
419 #ifndef SINGLE_USER
420 if (os_testing_mode())
421 {
422 if (!ngonzos)
423 {
424 sub_context_ty *scp;
425
426 scp = sub_context_new();
427 sub_var_set_charstar
428 (
429 scp,
430 "Name",
431 arglex_token_name(arglex_token_library)
432 );
433 fatal_intl(scp, i18n("test mode needs $name"));
434 // NOTREACHED
435 sub_context_delete(scp);
436 }
437 max = ngonzos;
438 }
439 else
440 #endif
441 {
442 //
443 // always have the system one last
444 // (this is where locks are taken)
445 //
446 gonzo_library_append(configured_comdir());
447 max = ngonzos - 1;
448 }
449
450 //
451 // build a new environment variable
452 //
453 string_list_ty path;
454 for (j = 0; j < max; ++j)
455 path.push_back(gonzo[j]->dir);
456 s1 = path.unsplit(":");
457 env_set("AEGIS_PATH", s1->str_text);
458 str_free(s1);
459
460 //
461 // zap the obsolete one, if present
462 //
463 s1 = str_from_c(progname_get());
464 s2 = str_upcase(s1);
465 str_free(s1);
466 env_unset(s2->str_text);
467 str_free(s2);
468
469 //
470 // do not repeat
471 //
472 done_tail = 1;
473 trace(("}\n"));
474 }
475
476
477 static gonzo_ty *
gonzo_nth(size_t j)478 gonzo_nth(size_t j)
479 {
480 gonzo_ty *result;
481
482 trace(("gonzo_nth(j = %ld)\n{\n", (long)j));
483 do_tail();
484 if (j >= ngonzos)
485 result = 0;
486 else
487 result = gonzo[j];
488 trace(("return %p;\n", result));
489 trace(("}\n"));
490 return result;
491 }
492
493
494 void
gonzo_gstate_write(void)495 gonzo_gstate_write(void)
496 {
497 size_t j;
498 gonzo_ty *gp;
499
500 trace(("gonzo_gstate_write()\n{\n"));
501 for (j = 0;; ++j)
502 {
503 gp = gonzo_nth(j);
504 if (!gp)
505 break;
506 gonzo_gstate_write_sub(gp);
507 }
508 trace(("}\n"));
509 }
510
511
512 static string_ty *
gonzo_project_home_path_sub(gonzo_ty * gp,string_ty * name)513 gonzo_project_home_path_sub(gonzo_ty *gp, string_ty *name)
514 {
515 gstate_ty *gstate_data;
516 size_t j;
517 string_ty *result;
518 gstate_where_ty *addr = 0;
519
520 //
521 // find the project in the gstate
522 //
523 trace(("gonzo_project_home_path_sub(gp = %p, name = \"%s\")\n{\n",
524 gp, name->str_text));
525 gstate_data = gonzo_gstate_get(gp);
526 assert(gstate_data->where);
527 result = 0;
528 for (j = 0; j < gstate_data->where->length; ++j)
529 {
530 addr = gstate_data->where->list[j];
531 if (addr->alias_for)
532 continue;
533 if (str_equal(addr->project_name, name))
534 {
535 result = addr->directory;
536 break;
537 }
538 }
539 trace(("return \"%s\";\n", (result ? result->str_text : "")));
540 trace(("}\n"));
541 return result;
542 }
543
544
545 string_ty *
gonzo_project_home_path_from_name(string_ty * name)546 gonzo_project_home_path_from_name(string_ty *name)
547 {
548 gonzo_ty *gp;
549 size_t j;
550 string_ty *result;
551 string_ty *s;
552
553 //
554 // find the project in the gstate list
555 //
556 trace(("gonzo_project_home_path_from_name(name = \"%s\")\n{\n",
557 name->str_text));
558 result = 0;
559 for (j = 0;; ++j)
560 {
561 gp = gonzo_nth(j);
562 if (!gp)
563 break;
564 s = gonzo_project_home_path_sub(gp, name);
565 if (s)
566 {
567 result = s;
568 break;
569 }
570 }
571 trace(("return \"%s\";\n", (result ? result->str_text : "")));
572 trace(("}\n"));
573 return result;
574 }
575
576
577 static string_ty *
gonzo_alias_to_actual_sub(gonzo_ty * gp,string_ty * name)578 gonzo_alias_to_actual_sub(gonzo_ty *gp, string_ty *name)
579 {
580 gstate_ty *gstate_data;
581 size_t j;
582 string_ty *result;
583
584 //
585 // find the project in the gstate
586 //
587 trace(("gonzo_alias_to_actual_sub(gp = %p, name = \"%s\")\n{\n",
588 gp, name->str_text));
589 gstate_data = gonzo_gstate_get(gp);
590 assert(gstate_data->where);
591 result = 0;
592 for (j = 0; j < gstate_data->where->length; ++j)
593 {
594 gstate_where_ty *addr;
595
596 addr = gstate_data->where->list[j];
597 if (!addr->alias_for)
598 continue;
599 if (str_equal(addr->project_name, name))
600 {
601 result = addr->alias_for;
602 break;
603 }
604 }
605 trace(("return \"%s\";\n", (result ? result->str_text : "")));
606 trace(("}\n"));
607 return result;
608 }
609
610
611 string_ty *
gonzo_alias_to_actual(string_ty * name)612 gonzo_alias_to_actual(string_ty *name)
613 {
614 gonzo_ty *gp;
615 size_t j;
616 string_ty *result;
617 string_ty *s;
618
619 //
620 // find the project in the gstate list
621 //
622 trace(("gonzo_alias_to_actual(name = \"%s\")\n{\n", name->str_text));
623 result = 0;
624 for (j = 0;; ++j)
625 {
626 gp = gonzo_nth(j);
627 if (!gp)
628 break;
629 s = gonzo_alias_to_actual_sub(gp, name);
630 if (s)
631 {
632 result = s;
633 break;
634 }
635 }
636 trace(("return \"%s\";\n", (result ? result->str_text : "")));
637 trace(("}\n"));
638 return result;
639 }
640
641
642 void
gonzo_project_list(string_list_ty * result)643 gonzo_project_list(string_list_ty *result)
644 {
645 size_t n;
646 size_t j;
647 gonzo_ty *gp;
648 gstate_ty *gstate_data;
649
650 trace(("gonzo_project_list(result = %p)\n{\n", result));
651 result->clear();
652 for (n = 0;; ++n)
653 {
654 gp = gonzo_nth(n);
655 if (!gp)
656 break;
657
658 //
659 // read the gstate file
660 //
661 gstate_data = gonzo_gstate_get(gp);
662
663 //
664 // list the projects
665 //
666 assert(gstate_data->where);
667 for (j = 0; j < gstate_data->where->length; ++j)
668 {
669 gstate_where_ty *addr;
670
671 addr = gstate_data->where->list[j];
672 if (addr->alias_for)
673 continue;
674 result->push_back_unique(addr->project_name);
675 }
676 }
677 trace(("}\n"));
678 }
679
680
681 void
gonzo_alias_list(string_list_ty * result)682 gonzo_alias_list(string_list_ty *result)
683 {
684 size_t n;
685 size_t j;
686 gonzo_ty *gp;
687 gstate_ty *gstate_data;
688
689 trace(("gonzo_project_list(result = %p)\n{\n", result));
690 result->clear();
691 for (n = 0;; ++n)
692 {
693 gp = gonzo_nth(n);
694 if (!gp)
695 break;
696
697 //
698 // read the gstate file
699 //
700 gstate_data = gonzo_gstate_get(gp);
701
702 //
703 // list the projects
704 //
705 assert(gstate_data->where);
706 for (j = 0; j < gstate_data->where->length; ++j)
707 {
708 gstate_where_ty *addr;
709
710 addr = gstate_data->where->list[j];
711 if (!addr->alias_for)
712 continue;
713 result->push_back_unique(addr->project_name);
714 }
715 }
716 trace(("}\n"));
717 }
718
719
720 void
gonzo_project_list_user(const nstring & uname,nstring_list & result)721 gonzo_project_list_user(const nstring &uname, nstring_list &result)
722 {
723 trace(("gonzo_project_list_user(uname = %s, result = @%p)\n{\n",
724 uname.quote_c().c_str(), &result));
725 result.clear();
726 for (size_t n = 0;; ++n)
727 {
728 gonzo_ty *gp = gonzo_nth(n);
729 if (!gp)
730 break;
731
732 //
733 // check out the ustate
734 //
735 nstring ustate_path =
736 nstring::format("%s/user/%s", gp->dir->str_text, uname.c_str());
737 trace(("ustate_path = %s;\n", ustate_path.quote_c().c_str()));
738 gonzo_become();
739 bool ok = os_exists(ustate_path);
740 gonzo_become_undo();
741 if (!ok)
742 continue;
743
744 gonzo_become();
745 ustate_ty *ustate_data = ustate_read_file(ustate_path);
746 gonzo_become_undo();
747 if (!ustate_data->own)
748 {
749 ustate_data->own =
750 (ustate_own_list_ty *)ustate_own_list_type.alloc();
751 }
752
753 //
754 // collect all projects this user owns changes in
755 //
756 for (size_t j = 0; j < ustate_data->own->length; ++j)
757 {
758 nstring pn(ustate_data->own->list[j]->project_name);
759 trace(("remember %s;\n", pn.quote_c().c_str()));
760 result.push_back_unique(pn);
761 }
762 ustate_type.free(ustate_data);
763 }
764 trace(("found %zd items\n", result.size()));
765 trace(("}\n"));
766 }
767
768
769 static int
sort_gstate_cmp(const void * va,const void * vb)770 sort_gstate_cmp(const void *va, const void *vb)
771 {
772 const gstate_where_ty *a = *(const gstate_where_ty **)va;
773 const gstate_where_ty *b = *(const gstate_where_ty **)vb;
774 return strcmp(a->project_name->str_text, b->project_name->str_text);
775 }
776
777
778 static void
sort_gstate_where(gonzo_ty * gp)779 sort_gstate_where(gonzo_ty *gp)
780 {
781 assert(gp);
782 gstate_ty *gstate_data = gonzo_gstate_get(gp);
783 assert(gstate_data);
784 assert(gstate_data->where);
785 assert(gstate_data->where->length > 0);
786 qsort
787 (
788 gstate_data->where->list,
789 gstate_data->where->length,
790 sizeof(gstate_data->where->list[0]),
791 sort_gstate_cmp
792 );
793 }
794
795
796 void
gonzo_project_add(project * pp)797 gonzo_project_add(project *pp)
798 {
799 trace(("gonzo_project_add(pp = %p)\n{\n", pp));
800 gonzo_ty *gp = gonzo_nth(0);
801 gstate_ty *gstate_data = gonzo_gstate_get(gp);
802 meta_type *type_p = 0;
803 gstate_where_ty **addr_p =
804 (gstate_where_ty **)
805 gstate_where_list_type.list_parse(gstate_data->where, &type_p);
806 assert(type_p == &gstate_where_type);
807 gstate_where_ty *addr = (gstate_where_ty *)gstate_where_type.alloc();
808 *addr_p = addr;
809 trace_pointer(addr);
810 addr->project_name = project_name_get(pp).get_ref_copy();
811 addr->directory = str_copy(pp->home_path_get());
812 gp->modified = 1;
813 sort_gstate_where(gp);
814 trace(("}\n"));
815 }
816
817
818 void
gonzo_alias_add(project * pp,string_ty * name)819 gonzo_alias_add(project *pp, string_ty *name)
820 {
821 trace(("gonzo_alias_add(pp = %p)\n{\n", pp));
822 gonzo_ty *gp = gonzo_nth(0);
823 gstate_ty *gstate_data = gonzo_gstate_get(gp);
824 meta_type *type_p = 0;
825 gstate_where_ty **addr_p =
826 (gstate_where_ty **)
827 gstate_where_list_type.list_parse(gstate_data->where, &type_p);
828 assert(type_p == &gstate_where_type);
829 gstate_where_ty *addr = (gstate_where_ty *)gstate_where_type.alloc();
830 *addr_p = addr;
831 trace_pointer(addr);
832 addr->project_name = str_copy(name);
833 addr->alias_for = project_name_get(pp).get_ref_copy();
834 gp->modified = 1;
835 sort_gstate_where(gp);
836 trace(("}\n"));
837 }
838
839
840 static int
gonzo_project_delete_sub(gonzo_ty * gp,project * pp)841 gonzo_project_delete_sub(gonzo_ty *gp, project *pp)
842 {
843 gstate_ty *gstate_data;
844 size_t j;
845 int result;
846
847 //
848 // find the project in the gstate
849 //
850 trace(("gonzo_project_delete_sub(gp = %p, pp = %p)\n{\n",
851 gp, pp));
852 gstate_data = gonzo_gstate_get(gp);
853 assert(gstate_data->where);
854 result = 0;
855 for (j = 0; j < gstate_data->where->length; ++j)
856 {
857 gstate_where_ty *addr;
858 size_t k;
859
860 addr = gstate_data->where->list[j];
861 if (addr->alias_for)
862 continue;
863 if (nstring(addr->project_name) != project_name_get(pp))
864 continue;
865
866 //
867 // delete the item from the list
868 // (keep the list sorted)
869 //
870 for (k = j + 1; k < gstate_data->where->length; ++k)
871 gstate_data->where->list[k - 1] = gstate_data->where->list[k];
872 gstate_data->where->length--;
873
874 //
875 // free the item
876 //
877 gstate_where_type.free(addr);
878
879 //
880 // mark this gstate file as modified
881 //
882 gp->modified = 1;
883 result = 1;
884 break;
885 }
886 trace(("return %d;\n", result));
887 trace(("}\n"));
888 return result;
889 }
890
891
892 static int
is_leading_prefix(string_ty * s1,string_ty * s2)893 is_leading_prefix(string_ty *s1, string_ty *s2)
894 {
895 if (str_equal(s1, s2))
896 return 1;
897 return
898 (
899 s1->str_length < s2->str_length
900 &&
901 ispunct((unsigned char)s2->str_text[s1->str_length])
902 &&
903 !memcmp(s1->str_text, s2->str_text, s1->str_length)
904 );
905 }
906
907
908 static void
gonzo_alias_delete_destination_sub(gonzo_ty * gp,string_ty * name)909 gonzo_alias_delete_destination_sub(gonzo_ty *gp, string_ty *name)
910 {
911 gstate_ty *gstate_data;
912 size_t j;
913
914 //
915 // find the project in the gstate
916 //
917 trace(("gonzo_alias_delete_destination_sub(gp = %p, name = \"%s\")\n{\n",
918 gp, name->str_text));
919 gstate_data = gonzo_gstate_get(gp);
920 assert(gstate_data->where);
921 for (j = 0; j < gstate_data->where->length; ++j)
922 {
923 gstate_where_ty *addr;
924 size_t k;
925
926 addr = gstate_data->where->list[j];
927 if (!addr->alias_for)
928 continue;
929 if (!is_leading_prefix(name, addr->alias_for))
930 continue;
931
932 trace_string (addr->alias_for->str_text);
933 //
934 // delete the item from the list
935 //
936 for (k = j + 1; k < gstate_data->where->length; ++k)
937 gstate_data->where->list[k - 1] = gstate_data->where->list[k];
938 gstate_data->where->length--;
939 --j;
940
941 //
942 // free the item
943 //
944 gstate_where_type.free(addr);
945
946 //
947 // mark this gstate file as modified
948 //
949 gp->modified = 1;
950 }
951 trace(("}\n"));
952 }
953
954
955 void
gonzo_project_delete(project * pp)956 gonzo_project_delete(project *pp)
957 {
958 gonzo_ty *gp;
959 long j;
960
961 trace(("gonzo_project_delete(pp = %p)\n{\n", pp));
962 for (j = 0;; ++j)
963 {
964 gp = gonzo_nth(j);
965 if (!gp)
966 break;
967 gonzo_alias_delete_destination_sub(gp, project_name_get(pp).get_ref());
968 if (gonzo_project_delete_sub(gp, pp))
969 break;
970 }
971 trace(("}\n"));
972 }
973
974
975 static int
gonzo_alias_delete_sub(gonzo_ty * gp,string_ty * name)976 gonzo_alias_delete_sub(gonzo_ty *gp, string_ty *name)
977 {
978 gstate_ty *gstate_data;
979 size_t j;
980 int result;
981
982 //
983 // find the project in the gstate
984 //
985 trace(("gonzo_alias_delete_sub(gp = %p, name = \"%s\")\n{\n",
986 gp, name->str_text));
987 gstate_data = gonzo_gstate_get(gp);
988 assert(gstate_data->where);
989 result = 0;
990 for (j = 0; j < gstate_data->where->length; ++j)
991 {
992 gstate_where_ty *addr;
993 size_t k;
994
995 addr = gstate_data->where->list[j];
996 if (!addr->alias_for)
997 continue;
998 if (!str_equal(addr->project_name, name))
999 continue;
1000
1001 //
1002 // delete the item from the list
1003 //
1004 for (k = j + 1; k < gstate_data->where->length; ++k)
1005 gstate_data->where->list[k - 1] = gstate_data->where->list[k];
1006 gstate_data->where->length--;
1007
1008 //
1009 // free the item
1010 //
1011 gstate_where_type.free(addr);
1012
1013 //
1014 // mark this gstate file as modified
1015 //
1016 gp->modified = 1;
1017 result = 1;
1018 break;
1019 }
1020 trace(("return %d;\n", result));
1021 trace(("}\n"));
1022 return result;
1023 }
1024
1025
1026 void
gonzo_alias_delete(string_ty * name)1027 gonzo_alias_delete(string_ty *name)
1028 {
1029 gonzo_ty *gp;
1030 long j;
1031
1032 trace(("gonzo_alias_delete(name = \"%s\")\n{\n", name->str_text));
1033 for (j = 0;; ++j)
1034 {
1035 gp = gonzo_nth(j);
1036 if (!gp)
1037 break;
1038 gonzo_alias_delete_destination_sub(gp, name);
1039 if (gonzo_alias_delete_sub(gp, name))
1040 break;
1041 }
1042 trace(("}\n"));
1043 }
1044
1045
1046 static void
waiting_callback(void *)1047 waiting_callback(void *)
1048 {
1049 if (user_ty::create()->lock_wait())
1050 error_intl(0, i18n("waiting for global state lock"));
1051 else
1052 fatal_intl(0, i18n("global state lock not available"));
1053 }
1054
1055
1056 void
gonzo_gstate_lock_prepare_new(void)1057 gonzo_gstate_lock_prepare_new(void)
1058 {
1059 trace(("gonzo_gstate_lock_prepare_new()\n{\n"));
1060 lock_prepare_gstate(waiting_callback, 0);
1061 trace(("}\n"));
1062 }
1063
1064
1065 string_ty *
gonzo_lockpath_get(void)1066 gonzo_lockpath_get(void)
1067 {
1068 static string_ty *path;
1069 gonzo_ty *gp;
1070
1071 trace(("gonzo_lockpath_get()\n{\n"));
1072 if (!path)
1073 {
1074 do_tail();
1075 gp = gonzo[ngonzos - 1];
1076 construct_library_directory(gp);
1077 path = str_format("%s/lockfile", gp->dir->str_text);
1078 }
1079 trace(("return \"%s\";\n", path->str_text));
1080 trace(("}\n"));
1081 return path;
1082 }
1083
1084
1085 static int
gonzo_ustate_path_sub(gonzo_ty * gp,string_ty * project_name)1086 gonzo_ustate_path_sub(gonzo_ty *gp, string_ty *project_name)
1087 {
1088 gstate_ty *gstate_data;
1089 size_t j;
1090 int result;
1091
1092 //
1093 // find the project in the gstate
1094 //
1095 trace(("gonzo_ustate_path_sub(gp = %p)\n{\n", gp));
1096 gstate_data = gonzo_gstate_get(gp);
1097 assert(gstate_data->where);
1098 result = 0;
1099 for (j = 0; j < gstate_data->where->length; ++j)
1100 {
1101 gstate_where_ty *addr;
1102
1103 addr = gstate_data->where->list[j];
1104 if (addr->alias_for)
1105 continue;
1106 if (str_equal(addr->project_name, project_name))
1107 {
1108 result = 1;
1109 break;
1110 }
1111 }
1112 trace(("return %d;\n", result));
1113 trace(("}\n"));
1114 return result;
1115 }
1116
1117
1118 nstring
gonzo_ustate_path(const nstring & project_name,const nstring & login_name)1119 gonzo_ustate_path(const nstring &project_name, const nstring &login_name)
1120 {
1121 //
1122 // find the project in the gstate list
1123 // the user state file contains an index into the project files
1124 // and is thus kept in the same directory
1125 //
1126 trace(("gonzo_ustate_path(project_name = %s, login_name = %s)\n{\n",
1127 project_name.quote_c().c_str(), login_name.quote_c().c_str()));
1128 gonzo_ty *gp = 0;
1129 for (size_t j = 0;; ++j)
1130 {
1131 gp = gonzo_nth(j);
1132 if (!gp)
1133 {
1134 fatal_raw("project %s unknown (bug)",
1135 project_name.quote_c().c_str());
1136 }
1137 if (gonzo_ustate_path_sub(gp, project_name.get_ref()))
1138 break;
1139 }
1140
1141 //
1142 // make sure that the directory for the user state files exists
1143 // (must be world writable in testing situations)
1144 //
1145 gonzo_become();
1146 assert(os_exists(gp->dir));
1147 nstring dir = nstring::format("%s/user", gp->dir->str_text);
1148 if (!os_exists(dir))
1149 {
1150 os_mkdir(dir, 0755);
1151 if (gp->temporary)
1152 os_chmod(dir, 0777);
1153 }
1154 gonzo_become_undo();
1155
1156 //
1157 // build the user state file name
1158 //
1159 nstring result =
1160 nstring::format("%s/user/%s", gp->dir->str_text, login_name.c_str());
1161 trace(("return %s;\n", result.quote_c().c_str()));
1162 trace(("}\n"));
1163 return result;
1164 }
1165
1166
1167 void
gonzo_become(void)1168 gonzo_become(void)
1169 {
1170 trace(("gonzo_become()\n{\n"));
1171 gonzo_user()->become_begin();
1172 trace(("}\n"));
1173 }
1174
1175
1176 void
gonzo_become_undo(void)1177 gonzo_become_undo(void)
1178 {
1179 trace(("gonzo_become_undo()\n{\n"));
1180 gonzo_user()->become_end();
1181 trace(("}\n"));
1182 }
1183
1184
1185 void
gonzo_report_path(string_list_ty * p)1186 gonzo_report_path(string_list_ty *p)
1187 {
1188 long j;
1189 gonzo_ty *gp;
1190 string_ty *s;
1191
1192 p->clear();
1193 for (j = 0;; ++j)
1194 {
1195 gp = gonzo_nth(j);
1196 if (!gp)
1197 break;
1198 p->push_back_unique(gp->dir);
1199 }
1200
1201 s = str_from_c(configured_datadir());
1202 p->push_back_unique(s);
1203 str_free(s);
1204 }
1205
1206
1207 int
gonzo_alias_acceptable(string_ty * name)1208 gonzo_alias_acceptable(string_ty *name)
1209 {
1210 string_ty *s = str_quote_shell(name);
1211 bool ok = str_equal(s, name);
1212 str_free(s);
1213 return ok;
1214 }
1215
1216
1217 // vim: set ts=8 sw=4 et :
1218