//
// aegis - project change supervisor
// Copyright (C) 1991-2014 Peter Miller
// Copyright (C) 2007-2009 Walter Franzini
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct gonzo_ty
{
string_ty *dir;
string_ty *gstate_filename;
gstate_ty *gstate_data;
int is_a_new_file;
int modified;
long lock_magic;
int temporary;
};
static size_t ngonzos;
static size_t ngonzos_max;
static gonzo_ty **gonzo;
static int done_tail;
static user_ty::pointer
gonzo_user(void)
{
static user_ty::pointer u;
if (!u)
{
u = user_ty::create(configured_aegis_uid(), configured_aegis_gid());
u->umask_set(022);
if (u->get_uid() >= AEGIS_MIN_UID)
{
sub_context_ty *scp;
scp = sub_context_new();
sub_var_set_long(scp, "Number1", u->get_uid());
sub_var_set_long(scp, "Number2", AEGIS_MIN_UID);
fatal_intl
(
scp,
i18n("AEGIS_USER_UID ($number1) vs AEGIS_MIN_UID ($number2) misconfigured")
);
// NOTREACHED
sub_context_delete(scp);
}
if (u->get_gid() >= AEGIS_MIN_GID)
{
sub_context_ty *scp;
scp = sub_context_new();
sub_var_set_long(scp, "Number1", u->get_gid());
sub_var_set_long(scp, "Number2", AEGIS_MIN_GID);
fatal_intl
(
scp,
i18n("AEGIS_USER_GID ($number1) vs AEGIS_MIN_GID ($number2) misconfigured")
);
// NOTREACHED
sub_context_delete(scp);
}
}
return u;
}
static int
is_temporary(string_ty *s)
{
return !!strstr(s->str_text, "/tmp/");
}
void
gonzo_library_append(const char *s)
{
gonzo_ty *gp;
string_ty *tmp;
string_ty *dir;
//
// resolve the pathname
//
trace(("gonzo_library_append(s = \"%s\")\n{\n", s));
assert(s);
assert(!done_tail);
tmp = str_from_c(s);
os_become_orig();
dir = os_pathname(tmp, 1);
os_become_undo();
str_free(tmp);
//
// append the new entry to the end of the list
//
gp = (gonzo_ty *)mem_alloc_clear(sizeof(gonzo_ty));
gp->dir = dir;
gp->temporary = is_temporary(dir);
gp->gstate_filename = str_format("%s/state", gp->dir->str_text);
trace(("gonzo = %p;\n", gonzo));
trace(("ngonzos = %ld;\n", (long)ngonzos));
if (ngonzos >= ngonzos_max)
{
ngonzos_max = ngonzos_max * 2 + 4;
gonzo_ty **new_gonzo = new gonzo_ty * [ngonzos_max];
for (size_t k = 0; k < ngonzos; ++k)
new_gonzo[k] = gonzo[k];
delete [] gonzo;
gonzo = new_gonzo;
}
gonzo[ngonzos++] = gp;
trace(("}\n"));
}
static void
lock_sync(gonzo_ty *gp)
{
trace(("lock_sync(gp = %p)\n{\n", gp));
long n = lock_magic();
if (gp->lock_magic == n)
goto ret;
assert(!gp->modified);
gp->lock_magic = n;
if (gp->gstate_data && !gp->is_a_new_file)
{
gstate_type.free(gp->gstate_data);
gp->gstate_data = 0;
}
ret:
trace(("}\n"));
}
static gstate_ty *
gonzo_gstate_get(gonzo_ty *gp)
{
trace(("gonzo_gstate_get(gp = %p)\n{\n", gp));
lock_sync(gp);
if (!gp->gstate_data)
{
//
// The problem here is if we get EACCES (Permission denied)
// for the lstat to see if the file exists. For now, we say
// that EACCESS is OK (that's what the second argument says)
// because most of the time this means we simply ignore global
// state files we can't read. But if there is a new project
// being created (the only time one of these ignored files will
// need writing) when the "new" file is opened, the EACCES will
// happen again, so nothing will be damaged, and the user will
// see an appropriate error.
//
gonzo_become();
gp->is_a_new_file = !os_exists(gp->gstate_filename, true);
if (!gp->is_a_new_file)
{
os_chown_check
(
gp->gstate_filename,
0644,
gonzo_user()->get_uid(),
(gp->temporary ? -1 : gonzo_user()->get_gid())
);
gp->gstate_data = gstate_read_file(gp->gstate_filename);
}
else
gp->gstate_data = (gstate_ty *)gstate_type.alloc();
if (!gp->gstate_data->where)
gp->gstate_data->where =
(gstate_where_list_ty *)gstate_where_list_type.alloc();
gonzo_become_undo();
}
trace(("return %p;\n", gp->gstate_data));
trace(("}\n"));
return gp->gstate_data;
}
//
// NAME
// construct_library_directory
//
// SYNOPSIS
// void construct_library_directory(gonzo_ty *gp);
//
// DESCRIPTION
// The construct_library_directory function is used to
// construct missing elements of the aegis library search path.
// These elements must be constructed with great care so that
// they may work on NFS mounted disks of "data-less" clients.
//
// The path up to, but not including, the library directory must
// be owned by the executing user. The actions will fail if the
// executing user does not have sufficient permissions, as
// one would expect.
//
// The library directory itself must be owned by AEGIS_USER.
// This is to defend against tampering and ignorance.
// Having this change of ownership requires some fast footwork
// with chmod's to allow AEGIS_USER temporary access.
//
// ARGUMENTS
// gp - library directory to act upon
//
// CAVEAT
// The chown() system call will not work on data-less clients,
// because the remote hosts which owns the NFS mounted disks
// will rarely trust the local "root" user, and map all
// foreign "root" users to the "unknown" user instead.
//
static void
construct_library_directory(gonzo_ty *gp)
{
int exists;
string_ty *above;
string_ty *above2;
string_ty *root;
int mode;
//
// If the library directory already exists,
// then we need do nothing.
//
os_become_orig();
exists = os_exists(gp->dir);
if (exists)
{
os_become_undo();
return;
}
//
// Construct all directories up to,
// but not including, the library directory,
// if they do not already exist.
// Construct these directories as the original user,
// possibly discovering relevant permission problems.
//
above = os_dirname(gp->dir);
above2 = str_n_from_c(above->str_text + 1, above->str_length - 1);
root = str_from_c("/");
os_mkdir_between(root, above2, 0755); // NOT setgid
str_free(root);
str_free(above2);
//
// Get the mode of the directory containing the library directory,
// so that we may restore it later. Change the mode to world
// writable, so that the library directory itself may be created
// belonging to gonzo.
//
mode = os_chmod_query(above);
mode |= 0111; // minimum: all may search
undo_chmod_errok(above, mode);
os_chmod(above, mode | 0777);
os_become_undo();
//
// Create the library directory itself belonging to gonzo,
// and make sure it is world accessable.
// (must be world writable in testing situations)
//
gonzo_become();
os_mkdir(gp->dir, 0755);
if (gp->temporary)
os_chmod(gp->dir, 0777);
gonzo_become_undo();
//
// Restore permissions for the containing directory.
//
os_become_orig();
os_chmod(above, mode);
os_become_undo();
str_free(above);
}
static void
gonzo_gstate_write_sub(gonzo_ty *gp)
{
string_ty *filename_new;
string_ty *filename_old;
static int count;
if (!gp->modified)
return;
trace(("gonzo_gstate_write_sub()\n{\n"));
assert(gp->gstate_data);
assert(gp->gstate_filename);
filename_new = str_format("%s,%d", gp->gstate_filename->str_text, ++count);
filename_old = str_format("%s,%d", gp->gstate_filename->str_text, ++count);
if (gp->is_a_new_file)
{
construct_library_directory(gp);
gonzo_become();
undo_unlink_errok(filename_new);
gstate_write_file(filename_new, gp->gstate_data, 0);
commit_rename(filename_new, gp->gstate_filename);
os_chmod(filename_new, 0644);
gonzo_become_undo();
}
else
{
gonzo_become();
undo_unlink_errok(filename_new);
gstate_write_file(filename_new, gp->gstate_data, 0);
commit_rename(gp->gstate_filename, filename_old);
commit_rename(filename_new, gp->gstate_filename);
commit_unlink_errok(filename_old);
os_chmod(filename_new, 0644);
gonzo_become_undo();
}
str_free(filename_new);
str_free(filename_old);
gp->modified = 0;
trace(("}\n"));
}
static void
do_tail(void)
{
string_ty *s1;
string_ty *s2;
char *cp;
size_t j;
size_t max;
//
// only do this once
//
if (done_tail)
return;
//
// fetch the environment variable
//
trace(("do_tail()\n{\n"));
cp = getenv("AEGIS_PATH");
if (!cp)
{
cp = getenv("AEGIS");
if (cp)
{
sub_context_ty *scp;
scp = sub_context_new();
sub_var_set_charstar(scp, "Name1", "AEGIS");
sub_var_set_charstar(scp, "Name2", "AEGIS_PATH");
verbose_intl
(
scp,
i18n("warning: $name1 is obsolete, use $name2 environment variable")
);
sub_context_delete(scp);
}
}
if (cp)
{
s1 = str_from_c(cp);
string_list_ty path;
path.split(s1, ":", true);
str_free(s1);
for (j = 0; j < path.size(); ++j)
{
s1 = path[j];
if (!os_testing_mode() || is_temporary(s1))
gonzo_library_append(s1->str_text);
}
}
#ifndef SINGLE_USER
if (os_testing_mode())
{
if (!ngonzos)
{
sub_context_ty *scp;
scp = sub_context_new();
sub_var_set_charstar
(
scp,
"Name",
arglex_token_name(arglex_token_library)
);
fatal_intl(scp, i18n("test mode needs $name"));
// NOTREACHED
sub_context_delete(scp);
}
max = ngonzos;
}
else
#endif
{
//
// always have the system one last
// (this is where locks are taken)
//
gonzo_library_append(configured_comdir());
max = ngonzos - 1;
}
//
// build a new environment variable
//
string_list_ty path;
for (j = 0; j < max; ++j)
path.push_back(gonzo[j]->dir);
s1 = path.unsplit(":");
env_set("AEGIS_PATH", s1->str_text);
str_free(s1);
//
// zap the obsolete one, if present
//
s1 = str_from_c(progname_get());
s2 = str_upcase(s1);
str_free(s1);
env_unset(s2->str_text);
str_free(s2);
//
// do not repeat
//
done_tail = 1;
trace(("}\n"));
}
static gonzo_ty *
gonzo_nth(size_t j)
{
gonzo_ty *result;
trace(("gonzo_nth(j = %ld)\n{\n", (long)j));
do_tail();
if (j >= ngonzos)
result = 0;
else
result = gonzo[j];
trace(("return %p;\n", result));
trace(("}\n"));
return result;
}
void
gonzo_gstate_write(void)
{
size_t j;
gonzo_ty *gp;
trace(("gonzo_gstate_write()\n{\n"));
for (j = 0;; ++j)
{
gp = gonzo_nth(j);
if (!gp)
break;
gonzo_gstate_write_sub(gp);
}
trace(("}\n"));
}
static string_ty *
gonzo_project_home_path_sub(gonzo_ty *gp, string_ty *name)
{
gstate_ty *gstate_data;
size_t j;
string_ty *result;
gstate_where_ty *addr = 0;
//
// find the project in the gstate
//
trace(("gonzo_project_home_path_sub(gp = %p, name = \"%s\")\n{\n",
gp, name->str_text));
gstate_data = gonzo_gstate_get(gp);
assert(gstate_data->where);
result = 0;
for (j = 0; j < gstate_data->where->length; ++j)
{
addr = gstate_data->where->list[j];
if (addr->alias_for)
continue;
if (str_equal(addr->project_name, name))
{
result = addr->directory;
break;
}
}
trace(("return \"%s\";\n", (result ? result->str_text : "")));
trace(("}\n"));
return result;
}
string_ty *
gonzo_project_home_path_from_name(string_ty *name)
{
gonzo_ty *gp;
size_t j;
string_ty *result;
string_ty *s;
//
// find the project in the gstate list
//
trace(("gonzo_project_home_path_from_name(name = \"%s\")\n{\n",
name->str_text));
result = 0;
for (j = 0;; ++j)
{
gp = gonzo_nth(j);
if (!gp)
break;
s = gonzo_project_home_path_sub(gp, name);
if (s)
{
result = s;
break;
}
}
trace(("return \"%s\";\n", (result ? result->str_text : "")));
trace(("}\n"));
return result;
}
static string_ty *
gonzo_alias_to_actual_sub(gonzo_ty *gp, string_ty *name)
{
gstate_ty *gstate_data;
size_t j;
string_ty *result;
//
// find the project in the gstate
//
trace(("gonzo_alias_to_actual_sub(gp = %p, name = \"%s\")\n{\n",
gp, name->str_text));
gstate_data = gonzo_gstate_get(gp);
assert(gstate_data->where);
result = 0;
for (j = 0; j < gstate_data->where->length; ++j)
{
gstate_where_ty *addr;
addr = gstate_data->where->list[j];
if (!addr->alias_for)
continue;
if (str_equal(addr->project_name, name))
{
result = addr->alias_for;
break;
}
}
trace(("return \"%s\";\n", (result ? result->str_text : "")));
trace(("}\n"));
return result;
}
string_ty *
gonzo_alias_to_actual(string_ty *name)
{
gonzo_ty *gp;
size_t j;
string_ty *result;
string_ty *s;
//
// find the project in the gstate list
//
trace(("gonzo_alias_to_actual(name = \"%s\")\n{\n", name->str_text));
result = 0;
for (j = 0;; ++j)
{
gp = gonzo_nth(j);
if (!gp)
break;
s = gonzo_alias_to_actual_sub(gp, name);
if (s)
{
result = s;
break;
}
}
trace(("return \"%s\";\n", (result ? result->str_text : "")));
trace(("}\n"));
return result;
}
void
gonzo_project_list(string_list_ty *result)
{
size_t n;
size_t j;
gonzo_ty *gp;
gstate_ty *gstate_data;
trace(("gonzo_project_list(result = %p)\n{\n", result));
result->clear();
for (n = 0;; ++n)
{
gp = gonzo_nth(n);
if (!gp)
break;
//
// read the gstate file
//
gstate_data = gonzo_gstate_get(gp);
//
// list the projects
//
assert(gstate_data->where);
for (j = 0; j < gstate_data->where->length; ++j)
{
gstate_where_ty *addr;
addr = gstate_data->where->list[j];
if (addr->alias_for)
continue;
result->push_back_unique(addr->project_name);
}
}
trace(("}\n"));
}
void
gonzo_alias_list(string_list_ty *result)
{
size_t n;
size_t j;
gonzo_ty *gp;
gstate_ty *gstate_data;
trace(("gonzo_project_list(result = %p)\n{\n", result));
result->clear();
for (n = 0;; ++n)
{
gp = gonzo_nth(n);
if (!gp)
break;
//
// read the gstate file
//
gstate_data = gonzo_gstate_get(gp);
//
// list the projects
//
assert(gstate_data->where);
for (j = 0; j < gstate_data->where->length; ++j)
{
gstate_where_ty *addr;
addr = gstate_data->where->list[j];
if (!addr->alias_for)
continue;
result->push_back_unique(addr->project_name);
}
}
trace(("}\n"));
}
void
gonzo_project_list_user(const nstring &uname, nstring_list &result)
{
trace(("gonzo_project_list_user(uname = %s, result = @%p)\n{\n",
uname.quote_c().c_str(), &result));
result.clear();
for (size_t n = 0;; ++n)
{
gonzo_ty *gp = gonzo_nth(n);
if (!gp)
break;
//
// check out the ustate
//
nstring ustate_path =
nstring::format("%s/user/%s", gp->dir->str_text, uname.c_str());
trace(("ustate_path = %s;\n", ustate_path.quote_c().c_str()));
gonzo_become();
bool ok = os_exists(ustate_path);
gonzo_become_undo();
if (!ok)
continue;
gonzo_become();
ustate_ty *ustate_data = ustate_read_file(ustate_path);
gonzo_become_undo();
if (!ustate_data->own)
{
ustate_data->own =
(ustate_own_list_ty *)ustate_own_list_type.alloc();
}
//
// collect all projects this user owns changes in
//
for (size_t j = 0; j < ustate_data->own->length; ++j)
{
nstring pn(ustate_data->own->list[j]->project_name);
trace(("remember %s;\n", pn.quote_c().c_str()));
result.push_back_unique(pn);
}
ustate_type.free(ustate_data);
}
trace(("found %zd items\n", result.size()));
trace(("}\n"));
}
static int
sort_gstate_cmp(const void *va, const void *vb)
{
const gstate_where_ty *a = *(const gstate_where_ty **)va;
const gstate_where_ty *b = *(const gstate_where_ty **)vb;
return strcmp(a->project_name->str_text, b->project_name->str_text);
}
static void
sort_gstate_where(gonzo_ty *gp)
{
assert(gp);
gstate_ty *gstate_data = gonzo_gstate_get(gp);
assert(gstate_data);
assert(gstate_data->where);
assert(gstate_data->where->length > 0);
qsort
(
gstate_data->where->list,
gstate_data->where->length,
sizeof(gstate_data->where->list[0]),
sort_gstate_cmp
);
}
void
gonzo_project_add(project *pp)
{
trace(("gonzo_project_add(pp = %p)\n{\n", pp));
gonzo_ty *gp = gonzo_nth(0);
gstate_ty *gstate_data = gonzo_gstate_get(gp);
meta_type *type_p = 0;
gstate_where_ty **addr_p =
(gstate_where_ty **)
gstate_where_list_type.list_parse(gstate_data->where, &type_p);
assert(type_p == &gstate_where_type);
gstate_where_ty *addr = (gstate_where_ty *)gstate_where_type.alloc();
*addr_p = addr;
trace_pointer(addr);
addr->project_name = project_name_get(pp).get_ref_copy();
addr->directory = str_copy(pp->home_path_get());
gp->modified = 1;
sort_gstate_where(gp);
trace(("}\n"));
}
void
gonzo_alias_add(project *pp, string_ty *name)
{
trace(("gonzo_alias_add(pp = %p)\n{\n", pp));
gonzo_ty *gp = gonzo_nth(0);
gstate_ty *gstate_data = gonzo_gstate_get(gp);
meta_type *type_p = 0;
gstate_where_ty **addr_p =
(gstate_where_ty **)
gstate_where_list_type.list_parse(gstate_data->where, &type_p);
assert(type_p == &gstate_where_type);
gstate_where_ty *addr = (gstate_where_ty *)gstate_where_type.alloc();
*addr_p = addr;
trace_pointer(addr);
addr->project_name = str_copy(name);
addr->alias_for = project_name_get(pp).get_ref_copy();
gp->modified = 1;
sort_gstate_where(gp);
trace(("}\n"));
}
static int
gonzo_project_delete_sub(gonzo_ty *gp, project *pp)
{
gstate_ty *gstate_data;
size_t j;
int result;
//
// find the project in the gstate
//
trace(("gonzo_project_delete_sub(gp = %p, pp = %p)\n{\n",
gp, pp));
gstate_data = gonzo_gstate_get(gp);
assert(gstate_data->where);
result = 0;
for (j = 0; j < gstate_data->where->length; ++j)
{
gstate_where_ty *addr;
size_t k;
addr = gstate_data->where->list[j];
if (addr->alias_for)
continue;
if (nstring(addr->project_name) != project_name_get(pp))
continue;
//
// delete the item from the list
// (keep the list sorted)
//
for (k = j + 1; k < gstate_data->where->length; ++k)
gstate_data->where->list[k - 1] = gstate_data->where->list[k];
gstate_data->where->length--;
//
// free the item
//
gstate_where_type.free(addr);
//
// mark this gstate file as modified
//
gp->modified = 1;
result = 1;
break;
}
trace(("return %d;\n", result));
trace(("}\n"));
return result;
}
static int
is_leading_prefix(string_ty *s1, string_ty *s2)
{
if (str_equal(s1, s2))
return 1;
return
(
s1->str_length < s2->str_length
&&
ispunct((unsigned char)s2->str_text[s1->str_length])
&&
!memcmp(s1->str_text, s2->str_text, s1->str_length)
);
}
static void
gonzo_alias_delete_destination_sub(gonzo_ty *gp, string_ty *name)
{
gstate_ty *gstate_data;
size_t j;
//
// find the project in the gstate
//
trace(("gonzo_alias_delete_destination_sub(gp = %p, name = \"%s\")\n{\n",
gp, name->str_text));
gstate_data = gonzo_gstate_get(gp);
assert(gstate_data->where);
for (j = 0; j < gstate_data->where->length; ++j)
{
gstate_where_ty *addr;
size_t k;
addr = gstate_data->where->list[j];
if (!addr->alias_for)
continue;
if (!is_leading_prefix(name, addr->alias_for))
continue;
trace_string (addr->alias_for->str_text);
//
// delete the item from the list
//
for (k = j + 1; k < gstate_data->where->length; ++k)
gstate_data->where->list[k - 1] = gstate_data->where->list[k];
gstate_data->where->length--;
--j;
//
// free the item
//
gstate_where_type.free(addr);
//
// mark this gstate file as modified
//
gp->modified = 1;
}
trace(("}\n"));
}
void
gonzo_project_delete(project *pp)
{
gonzo_ty *gp;
long j;
trace(("gonzo_project_delete(pp = %p)\n{\n", pp));
for (j = 0;; ++j)
{
gp = gonzo_nth(j);
if (!gp)
break;
gonzo_alias_delete_destination_sub(gp, project_name_get(pp).get_ref());
if (gonzo_project_delete_sub(gp, pp))
break;
}
trace(("}\n"));
}
static int
gonzo_alias_delete_sub(gonzo_ty *gp, string_ty *name)
{
gstate_ty *gstate_data;
size_t j;
int result;
//
// find the project in the gstate
//
trace(("gonzo_alias_delete_sub(gp = %p, name = \"%s\")\n{\n",
gp, name->str_text));
gstate_data = gonzo_gstate_get(gp);
assert(gstate_data->where);
result = 0;
for (j = 0; j < gstate_data->where->length; ++j)
{
gstate_where_ty *addr;
size_t k;
addr = gstate_data->where->list[j];
if (!addr->alias_for)
continue;
if (!str_equal(addr->project_name, name))
continue;
//
// delete the item from the list
//
for (k = j + 1; k < gstate_data->where->length; ++k)
gstate_data->where->list[k - 1] = gstate_data->where->list[k];
gstate_data->where->length--;
//
// free the item
//
gstate_where_type.free(addr);
//
// mark this gstate file as modified
//
gp->modified = 1;
result = 1;
break;
}
trace(("return %d;\n", result));
trace(("}\n"));
return result;
}
void
gonzo_alias_delete(string_ty *name)
{
gonzo_ty *gp;
long j;
trace(("gonzo_alias_delete(name = \"%s\")\n{\n", name->str_text));
for (j = 0;; ++j)
{
gp = gonzo_nth(j);
if (!gp)
break;
gonzo_alias_delete_destination_sub(gp, name);
if (gonzo_alias_delete_sub(gp, name))
break;
}
trace(("}\n"));
}
static void
waiting_callback(void *)
{
if (user_ty::create()->lock_wait())
error_intl(0, i18n("waiting for global state lock"));
else
fatal_intl(0, i18n("global state lock not available"));
}
void
gonzo_gstate_lock_prepare_new(void)
{
trace(("gonzo_gstate_lock_prepare_new()\n{\n"));
lock_prepare_gstate(waiting_callback, 0);
trace(("}\n"));
}
string_ty *
gonzo_lockpath_get(void)
{
static string_ty *path;
gonzo_ty *gp;
trace(("gonzo_lockpath_get()\n{\n"));
if (!path)
{
do_tail();
gp = gonzo[ngonzos - 1];
construct_library_directory(gp);
path = str_format("%s/lockfile", gp->dir->str_text);
}
trace(("return \"%s\";\n", path->str_text));
trace(("}\n"));
return path;
}
static int
gonzo_ustate_path_sub(gonzo_ty *gp, string_ty *project_name)
{
gstate_ty *gstate_data;
size_t j;
int result;
//
// find the project in the gstate
//
trace(("gonzo_ustate_path_sub(gp = %p)\n{\n", gp));
gstate_data = gonzo_gstate_get(gp);
assert(gstate_data->where);
result = 0;
for (j = 0; j < gstate_data->where->length; ++j)
{
gstate_where_ty *addr;
addr = gstate_data->where->list[j];
if (addr->alias_for)
continue;
if (str_equal(addr->project_name, project_name))
{
result = 1;
break;
}
}
trace(("return %d;\n", result));
trace(("}\n"));
return result;
}
nstring
gonzo_ustate_path(const nstring &project_name, const nstring &login_name)
{
//
// find the project in the gstate list
// the user state file contains an index into the project files
// and is thus kept in the same directory
//
trace(("gonzo_ustate_path(project_name = %s, login_name = %s)\n{\n",
project_name.quote_c().c_str(), login_name.quote_c().c_str()));
gonzo_ty *gp = 0;
for (size_t j = 0;; ++j)
{
gp = gonzo_nth(j);
if (!gp)
{
fatal_raw("project %s unknown (bug)",
project_name.quote_c().c_str());
}
if (gonzo_ustate_path_sub(gp, project_name.get_ref()))
break;
}
//
// make sure that the directory for the user state files exists
// (must be world writable in testing situations)
//
gonzo_become();
assert(os_exists(gp->dir));
nstring dir = nstring::format("%s/user", gp->dir->str_text);
if (!os_exists(dir))
{
os_mkdir(dir, 0755);
if (gp->temporary)
os_chmod(dir, 0777);
}
gonzo_become_undo();
//
// build the user state file name
//
nstring result =
nstring::format("%s/user/%s", gp->dir->str_text, login_name.c_str());
trace(("return %s;\n", result.quote_c().c_str()));
trace(("}\n"));
return result;
}
void
gonzo_become(void)
{
trace(("gonzo_become()\n{\n"));
gonzo_user()->become_begin();
trace(("}\n"));
}
void
gonzo_become_undo(void)
{
trace(("gonzo_become_undo()\n{\n"));
gonzo_user()->become_end();
trace(("}\n"));
}
void
gonzo_report_path(string_list_ty *p)
{
long j;
gonzo_ty *gp;
string_ty *s;
p->clear();
for (j = 0;; ++j)
{
gp = gonzo_nth(j);
if (!gp)
break;
p->push_back_unique(gp->dir);
}
s = str_from_c(configured_datadir());
p->push_back_unique(s);
str_free(s);
}
int
gonzo_alias_acceptable(string_ty *name)
{
string_ty *s = str_quote_shell(name);
bool ok = str_equal(s, name);
str_free(s);
return ok;
}
// vim: set ts=8 sw=4 et :