module/da/test\
module/debug\
module/tranche\
- module/dispatch
+ module/dispatch\
+ module/subu-0
.PHONY: all
all:
- $(foreach dir, $(MAKEABLE), \
- -make -C $$dir dist-clean dep lib exec share
- )
+ for dir in $(MAKEABLE); do pushd $$dir; make dist-clean dep lib exec share; popd; done
-.PHONY: all
+.PHONY: dep
+dep:
+ for dir in $(MAKEABLE); do pushd $$dir; make dep; popd; done
+
+.PHONY: update
update:
- $(foreach dir, $(MAKEABLE), \
- -make -C $$dir lib exec share
- )
+ for dir in $(MAKEABLE); do pushd $$dir; make lib exec share; popd; done
.PHONY: info
info:
- @echo "SRCDIRS:" $(SRCDIRS)
@echo "MAKEABLE:" $(MAKEABLE)
.PHONY: clean
for dir in $(MAKEABLE); do pushd $$dir; make clean; popd; done
.PHONY: dist-clean
-dist-clean :
- for dir in $(MAKEABLE); do pushd $$dir; make dist-clean; popd; done
+dist-clean : clean
for i in $(wildcard module/share/lib/*); do rm $$i; done
for i in $(wildcard module/share/inc/*); do rm $$i; done
for i in $(wildcard module/share/bin/*); do rm $$i; done
--- /dev/null
+#ifndef DA_LIB_H
+#define DA_LIB_H
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+typedef struct Da{
+ char *base;
+ char *end; // one byte/one element off the end of the array
+ size_t size; // size >= (end - base) + 1;
+ size_t element_size;
+} Da;
+
+#define RETURN(dap, r) \
+ { da_free_elements(dap); return r; }
+
+
+void da_alloc(Da *dap, size_t element_size);
+void da_free(Da *dap);
+void da_rewind(Da *dap);
+bool da_emptyq(Da *dap);
+size_t da_length(Da *dap);
+void da_rebase(Da *dap, char *old_base, void *pta);
+char *da_expand(Da *dap);
+bool da_boundq(Da *dap);
+
+char *da_index(Da *dap, size_t i);
+char *da_push_alloc(Da *dap);
+char *da_push(Da *dap, void *element);
+bool da_pop(Da *dap, void *element);
+
+bool da_endq(Da *dap, void *pt);
+void da_map(Da *dap, void f(void *, void *), void *closure);
+
+void da_free_elements(Da *dap);
+
+void da_ints_print(Da *dap, char *sep);
+
+void da_strings_print(Da *dap, char *sep);
+bool da_strings_exists(Da *string_arrp, char *test_string);
+void da_strings_set_insert(Da *string_arrp, char *proffered_string, void destruct(void *));
+void da_strings_set_union(Da *string_arrp, Da *proffered_string_arrp, void destruct(void *));
+
+
+char *da_string_input(Da *dap, FILE *file);
+void da_string_push(Da *dap0, char *string);
+
+void da_cat(Da *dap_base, Da *dap_cat);
+
+#endif
+
.PHONY: lib
lib:
- $(MAKE) $@
cp $(SRCDIR)/da.lib.h $(INCDIR)/da.h
+ $(MAKE) $@
.PHONY: exec
exec:
EXECDIR=exec
INCDIR=include
LIBDIR=lib
-SHAREDIR=../share
+SHAREDIR=$(realpath $(PROJECT_SUBU)/module/share)
SRCDIR=src
TESTDIR=test
TMPDIR=tmp
# compiler and flags
C=gcc
-CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB
-#CFLAGS=-std=gnu11 -fPIC -I. -Werror
-LINKFLAGS=-Llib -lda
+CFLAGS=-std=gnu11 -fPIC -I$(SRCDIR) -I$(INCDIR) -I$(SHAREDIR)/include -ggdb -Werror -DDEBUG -DDEBUGDB
+LINKFLAGS=-L$(LIBDIR) -l$(MODULE)
return pt;
}
-void da_push(Da *dap, void *element){
- if( dap->end >= dap->base + dap->size ) da_expand(dap);
- memcpy(dap->end, element, dap->element_size);
+// allocate space for a new element at the end of the array
+char *da_push_alloc(Da *dap){
+ size_t element_off = dap->end - dap->base;
dap->end += dap->element_size;
+ if( dap->end > dap->base + dap->size ) da_expand(dap);
+ return dap->base + element_off;
+}
+
+char *da_push(Da *dap, void *element){
+ char *element_pt = da_push_alloc(dap);
+ memcpy(element_pt, element, dap->element_size);
+ return element_pt;
}
bool da_pop(Da *dap, void *element){
} Da;
#define RETURN(dap, r) \
- { da_map(dap, da_free, NULL); return r; }
+ { da_free_elements(dap); return r; }
void da_alloc(Da *dap, size_t element_size);
bool da_boundq(Da *dap);
char *da_index(Da *dap, size_t i);
-void da_push(Da *dap, void *element);
+char *da_push_alloc(Da *dap);
+char *da_push(Da *dap, void *element);
bool da_pop(Da *dap, void *element);
bool da_endq(Da *dap, void *pt);
# compiler and flags
C=gcc
-CFLAGS=-std=gnu11 -fPIC -I../include -ggdb -Werror -DDEBUG -DDEBUGDB
-#CFLAGS=-std=gnu11 -fPIC -I../include -Werror
+CFLAGS=-std=gnu11 -fPIC -Iinclude -I../include -ggdb -Werror -DDEBUG -DDEBUGDB
+#CFLAGS=-std=gnu11 -fPIC -Iinclude -I../include -Werror
LINKFLAGS= -Llib -L../lib -ltest -lda
--- /dev/null
+#ifndef DB_LIB_H
+#define DB_LIB_H
+
+int debug_printf(const char *format, ...);
+
+#endif
.PHONY: lib
lib:
+ cp $(SRCDIR)/debug.lib.h $(INCDIR)/debug.h
$(MAKE) $@
- cp $(SRCDIR)/db.lib.h $(INCDIR)/db.h
%::
$(MAKE) $@
-MODULE=db
+MODULE=debug
DEPRDIR=deprecated
DOCDIR=doc
EXECDIR=exec
INCDIR=include
LIBDIR=lib
-SHAREDIR=../share
+SHAREDIR=$(realpath $(PROJECT_SUBU)/module/share)
SRCDIR=src
TESTDIR=test
TMPDIR=tmp
# compiler and flags
C=gcc
-CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB
-#CFLAGS=-std=gnu11 -fPIC -I. -Werror
-LINKFLAGS=-Llib -ldb
+CFLAGS=-std=gnu11 -fPIC -I$(SRCDIR) -I$(INCDIR) -I$(SHAREDIR)/include -ggdb -Werror -DDEBUG -DDEBUGDB
+LINKFLAGS=-L$(LIBDIR) -l$(MODULE)
--- /dev/null
+ /*
+ Runs a command or function as its own process.
+
+ The return status integer from command or function must be greater than ERR_DISPATCH.
+ In the case of dispatch_exec, we only have a promise from the user to not dispatch
+ non compliant commands.
+
+ */
+ #ifndef DISPATCH_LIB_H
+ #define DISPATCH_LIB_H
+ #include <sys/types.h>
+ #include <unistd.h>
+
+ #define ERR_DISPATCH -1024
+ #define ERR_DISPATCH_NEGATIVE_RETURN_STATUS -1024
+ #define ERR_DISPATCH_F_FORK -1025
+ #define ERR_DISPATCH_F_SETEUID -1026
+ #define ERR_DISPATCH_F_SETEGID -1027
+ #define ERR_DISPATCH_NULL_EXECUTABLE -1028
+ #define ERR_DISPATCH_EXEC -1029
+
+ // currently both dispatcher and dispatchee strings are statically allocated
+ struct dispatch_ctx{
+ char *dispatcher; // name of the dispatch function ("dispatch_f", "dispatch_f_euid_egid", etc.)
+ char *dispatchee; // name of the function being dispatched
+ int err; // error code as listed below, or status returned from dispatchee
+ };
+void dispatch_f_mess(char *fname, int err, char *dispatchee);
+int dispatch_f(char *fname, int (*f)(void *arg), void *f_arg);
+int dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid);
+int dispatch_exec(char **argv, char **envp);
+#endif
.PHONY: lib
lib:
- $(MAKE) $@
cp $(SRCDIR)/dispatch.lib.h $(INCDIR)/dispatch.h
+ $(MAKE) $@
%::
$(MAKE) $@
# compiler and flags
C=gcc
-CFLAGS=-std=gnu11 -fPIC -I. -I../share/include -ggdb -Werror -DDEBUG -DDEBUGDB
-#CFLAGS=-std=gnu11 -fPIC -I. -I../share/include -Werror
-
-LINKFLAGS=-L1_lib -lda
+CFLAGS=-std=gnu11 -fPIC -I$(SRCDIR) -I$(INCDIR) -I$(SHAREDIR)/include -ggdb -Werror -DDEBUG -DDEBUGDB
+LINKFLAGS=-L$(LIBDIR) -L(SHAREDIR)/lib -ldebug
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
-#include <db.h>
+#include <debug.h>
#include "dispatch.lib.h"
#tranche dispatch.lib.h
} Da;
#define RETURN(dap, r) \
- { da_map(dap, da_free, NULL); return r; }
+ { da_free_elements(dap); return r; }
void da_alloc(Da *dap, size_t element_size);
bool da_boundq(Da *dap);
char *da_index(Da *dap, size_t i);
-void da_push(Da *dap, void *element);
+char *da_push_alloc(Da *dap);
+char *da_push(Da *dap, void *element);
bool da_pop(Da *dap, void *element);
bool da_endq(Da *dap, void *pt);
+++ /dev/null
-#ifndef DB_LIB_H
-#define DB_LIB_H
-
-int debug_printf(const char *format, ...);
-
-#endif
--- /dev/null
+#ifndef DB_LIB_H
+#define DB_LIB_H
+
+int debug_printf(const char *format, ...);
+
+#endif
--- /dev/null
+ typedef unsigned int uint;
+ /*
+ Fedora 29's sss_cache is checking the inherited uid instead of the effective
+ uid, so setuid root scripts will fail when calling sss_cache.
+
+ Fedora 29's 'useradd' calls sss_cache, and useradd is called by our setuid root
+ program subu-mk-0.
+ */
+ #define BUG_SSS_CACHE_RUID 1
+ // extern char *DB_File;
+ extern char DB_File[];
+ extern uint Subuhome_Perms;
+ extern uint First_Max_Subunumber;
+ extern char Subuland_Extension[];
+ /*
+ The db file is maintained in SQLite
+
+ Because linux user names are limited length, subu user names are of a compact
+ form: _s<number>. A separate table translates the numbers into the subu names.
+
+ Each of these returns SQLITE_OK upon success
+ */
+ #include <sqlite3.h>
+ int db_begin(sqlite3 *db);
+ int db_commit(sqlite3 *db);
+ int db_rollback(sqlite3 *db);
+ int subudb_schema(sqlite3 *db);
+ int subudb_number_get(sqlite3 *db, int *n);
+ int subudb_number_set(sqlite3 *db, int n);
+ int subudb_Masteru_Subu_put(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username);
+ int subudb_Masteru_Subu_get_subu_username(sqlite3 *db, char *masteru_name, char *subuname, char **subu_username);
+ typedef struct{
+ char *subuname; // the name that masteru chose for his or her subu
+ char *subu_username; // the adduser name we gave it, typically of the s<number>
+ } subudb_subu_element;
+ int subudb_Masteru_Subu_get_subus(sqlite3 *db, char *masteru_name, Da *subus);
+ int subudb_Masteru_Subu_rm(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username);
+ #include <stdbool.h>
+ #include <errno.h>
+ #include <sqlite3.h>
+ char *useradd_mess(int err);
+ char *userdel_mess(int err);
+ #define SUBU_ERR_ARG_CNT 1
+ #define SUBU_ERR_SETUID_ROOT 2
+ #define SUBU_ERR_MALLOC 3
+ #define SUBU_ERR_MKDIR_SUBUHOME 4
+ #define SUBU_ERR_RMDIR_SUBUHOME 5
+ #define SUBU_ERR_SUBUNAME_MALFORMED 6
+ #define SUBU_ERR_HOMELESS 7
+ #define SUBU_ERR_DB_FILE 8
+ #define SUBU_ERR_SUBUHOME_EXISTS 9
+ #define SUBU_ERR_BUG_SSS 10
+ #define SUBU_ERR_FAILED_USERADD 11
+ #define SUBU_ERR_FAILED_USERDEL 12
+ #define SUBU_ERR_SUBU_NOT_FOUND 13
+ #define SUBU_ERR_N 14
+ #define SUBU_ERR_BIND 15
+ void subu_err(char *fname, int err, char *mess);
+ int subu_mk_0(char **mess, sqlite3 *db, char *subuname);
+ int subu_rm_0(char **mess, sqlite3 *db, char *subuname);
+ int subu_bind(char **mess, char *masteru_name, char *subu_username, char *subuhome);
+ int subu_bind_all(char **mess, sqlite3 *db);
SHELL=/bin/bash
MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tool/lib/makefile-cc
-#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tool/lib/makefile-cc
-include makefile-flags
-
.PHONY: all
all: version
.PHONY: info
info:
@echo "TRCDIR: " $(TRCDIR)
+ @echo "SHAREDIR: " $(SHAREDIR)
$(MAKE) $@
.PHONY: setup
.PHONY: dep
dep:
if [ -e $(DEPFILE) ]; then rm $(DEPFILE); fi
- @trcsources=$(wildcard $(TRCDIR)/*.trc.c)$(wildcard $(TRCDIR)/*.trc.cc);\
+ trcsources=($(wildcard $(TRCDIR)/*.trc.c)$(wildcard $(TRCDIR)/*.trc.cc));\
if [ ! -z "$$trcsources" ]; then\
- trctargets=$$(tranche-target $$trcsources -sep " " -tdir $(SRCDIR) );\
- $(ECHO) $$trcsources;\
- tranche-make $$trcsources -tdir $(SRCDIR) -mfile $(DEPFILE);\
+ trctargets=$$(tranche-target $${trcsources[@]} -sep " " -tdir $(SRCDIR) );\
+ $(ECHO) "trcsources: " $${trcsources[@]};\
+ $(ECHO) "trctargets: " $$trctargets;\
+ tranche-make $${trcsources[@]} -tdir $(SRCDIR) -mfile $(DEPFILE);\
$(MAKE) $$trctargets;\
fi
$(MAKE) $@
.PHONY: lib
lib:
+ cp $(SRCDIR)/common.lib.h $(INCDIR)/subu.h
+ cat $(SRCDIR)/subudb.lib.h >> $(INCDIR)/subu.h
+ cat $(SRCDIR)/subu.lib.h >> $(INCDIR)/subu.h
$(MAKE) $@
- cp $(SRCDIR)/dispatch.lib.h $(INCDIR)/dispatch.h
%::
+ @echo default target
$(MAKE) $@
-MODULE=dispatch
+MODULE=subu-0
DEPRDIR=deprecated
DOCDIR=doc
EXECDIR=exec
INCDIR=include
LIBDIR=lib
-SHAREDIR=../share
+SHAREDIR=$(realpath $(PROJECT_SUBU)/module/share)
SRCDIR=tmp
TESTDIR=test
TMPDIR=tmp
# compiler and flags
C=gcc
-CFLAGS=-std=gnu11 -fPIC -I. -I../share/include -ggdb -Werror -DDEBUG -DDEBUGDB
-#CFLAGS=-std=gnu11 -fPIC -I. -I../share/include -Werror
-
-LINKFLAGS=-L1_lib -lda
+CFLAGS=-std=gnu11 -fPIC -I$(SRCDIR) -I$(INCDIR) -I$(SHAREDIR)/include -ggdb -Werror -DDEBUG -DDEBUGDB
+LINKFLAGS=-L$(LIBDIR) -L$(SHAREDIR)/lib/ -l$(MODULE) -lda -ltranche -ldispatch
+++ /dev/null
-#tranche common.lib.c
-#include "common.lib.h"
-
-#tranche common.lib.h
- typedef unsigned int uint;
- /*
- Fedora 29's sss_cache is checking the inherited uid instead of the effective
- uid, so setuid root scripts will fail when calling sss_cache.
-
- Fedora 29's 'useradd' calls sss_cache, and useradd is called by our setuid root
- program subu-mk-0.
- */
- #define BUG_SSS_CACHE_RUID 1
-#tranche-end
-
-#tranche common.lib.h
- // extern char *DB_File;
- extern char DB_File[];
- extern uint Subuhome_Perms;
- extern uint First_Max_Subunumber;
- extern char Subuland_Extension[];
-#tranche-end
-// char *DB_File = "/etc/subudb";
-char DB_File[] = "subudb";
-uint Subuhome_Perms = 0700;
-uint First_Max_Subunumber = 114;
-char Subuland_Extension[] = "/subuland/";
-
-#tranche-end
--- /dev/null
+#tranche common.lib.c
+#include "common.lib.h"
+
+#tranche common.lib.h
+ typedef unsigned int uint;
+ /*
+ Fedora 29's sss_cache is checking the inherited uid instead of the effective
+ uid, so setuid root scripts will fail when calling sss_cache.
+
+ Fedora 29's 'useradd' calls sss_cache, and useradd is called by our setuid root
+ program subu-mk-0.
+ */
+ #define BUG_SSS_CACHE_RUID 1
+#tranche-end
+
+#tranche common.lib.h
+ // extern char *DB_File;
+ extern char DB_File[];
+ extern uint Subuhome_Perms;
+ extern uint First_Max_Subunumber;
+ extern char Subuland_Extension[];
+#tranche-end
+// char *DB_File = "/etc/subudb";
+char DB_File[] = "subudb";
+uint Subuhome_Perms = 0700;
+uint First_Max_Subunumber = 114;
+char Subuland_Extension[] = "/subuland/";
+
+#tranche-end
+++ /dev/null
-/*
-The db file is maintained in SQLite
-
-Because user names of are of limited length, subu user names are always named _s<number>.
-A separate table translates the numbers into the subu names.
-
-The first argument is the biggest subu number in the system, or one minus an
-starting point for subu numbering.
-
-currently a unit converted to base 10 will always fit in a 21 bit buffer.
-
-Each of these returns SQLITE_OK upon success
-*/
-#include "subudb.lib.h"
-
-#if INTERFACE
-#include <sqlite3.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdbool.h>
-
-//--------------------------------------------------------------------------------
-// sqlite transactions don't nest. There is a way to use save points, but still
-// we can't just nest transactions. Instead use these wrappers around the whole
-// of something that needs to be in a transaction.
-int db_begin(sqlite3 *db){
- return sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
-}
-int db_commit(sqlite3 *db){
- return sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
-}
-int db_rollback(sqlite3 *db){
- return sqlite3_exec(db, "ROLLBACK;", NULL, NULL, NULL);
-}
-
-//--------------------------------------------------------------------------------
-int subudb_schema(sqlite3 *db){
- int rc;
-
- { // build tables
- char sql[] =
- "CREATE TABLE Masteru_Subu(masteru_name TEXT, subuname TEXT, subu_username TEXT);"
- "CREATE TABLE Attribute_Int(attribute TEXT, value INT);"
- ;
- rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
- if(rc != SQLITE_OK) return rc;
- }
-
- { // data initialization
- char *sql = "INSERT INTO Attribute_Int (attribute, value) VALUES ('Max_Subunumber', ?1);";
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
- sqlite3_bind_int(stmt, 1, First_Max_Subunumber);
- rc = sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- if( rc != SQLITE_DONE ) return rc;
- }
-
- return SQLITE_OK;
-}
-
-//--------------------------------------------------------------------------------
-int subudb_number_get(sqlite3 *db, int *n){
- char *sql = "SELECT value FROM Attribute_Int WHERE attribute = 'Max_Subunumber';";
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
- int rc = sqlite3_step(stmt);
- if( rc == SQLITE_ROW ){
- *n = sqlite3_column_int(stmt,0);
- rc = sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- if( rc != SQLITE_DONE ) return rc;
- return SQLITE_OK;
- }
- // should have a message return, suppose
- sqlite3_finalize(stmt);
- return SQLITE_NOTFOUND;
-}
-
-int subudb_number_set(sqlite3 *db, int n){
- int rc;
- char *sql = "UPDATE Attribute_Int SET value = ?1 WHERE attribute = 'Max_Subunumber';";
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
- sqlite3_bind_int(stmt, 1, n);
- rc = sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- if( rc == SQLITE_DONE ) return SQLITE_OK;
- return rc;
-}
-
-//--------------------------------------------------------------------------------
-// put relation into Masteru_Subu table
-int subudb_Masteru_Subu_put(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){
- char *sql = "INSERT INTO Masteru_Subu VALUES (?1, ?2, ?3);";
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
- sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC);
- sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC);
- sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC);
- int rc = sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- if( rc == SQLITE_DONE ) return SQLITE_OK;
- return rc;
-}
-
-//--------------------------------------------------------------------------------
-int subudb_Masteru_Subu_get_subu_username(sqlite3 *db, char *masteru_name, char *subuname, char **subu_username){
- char *sql = "SELECT subu_username FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2;";
- size_t sql_len = strlen(sql);
- sqlite3_stmt *stmt;
- int rc;
- rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL);
- if( rc != SQLITE_OK ) return rc;
- sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC);
- sqlite3_bind_text(stmt, 2, subuname, strlen(subuname), SQLITE_STATIC);
- rc = sqlite3_step(stmt);
- if( rc == SQLITE_ROW ){
- const char *username = sqlite3_column_text(stmt, 0);
- *subu_username = strdup(username);
- }else{
- sqlite3_finalize(stmt);
- return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE
- }
- rc = sqlite3_step(stmt);
- if( rc == SQLITE_DONE ) return SQLITE_OK;
- return rc;
-}
-
-//--------------------------------------------------------------------------------
-
-// we return and array of subudb_subu_info
-#if INTERFACE
-struct subudb_subu_element{
- char *subuname;
- char *subu_username;
-};
-#endif
-
-int subudb_Masteru_Subu_get_subus
-(
- sqlite3 *db,
- char *masteru_name,
- da *subus
-){
- char *sql = "SELECT subuname, subu_username"
- " FROM Masteru_Subu"
- " WHERE masteru_name = ?1;";
- size_t sql_len = strlen(sql);
- sqlite3_stmt *stmt;
- int rc;
- rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL);
- if( rc != SQLITE_OK ) return rc;
- sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC);
-
- da_alloc(subus, sizeof(subudb_subu_element));
- subudb_subu_element *pt = (subudb_subu_element *)subus->base;
- rc = sqlite3_step(stmt);
- while( rc == SQLITE_ROW ){
- if( da_boundq(subus, pt) ){
- char *old_base = da_expand(subus);
- da_rebase(subus, old_base, pt);
- }
- pt->subuname = strdup(sqlite3_column_text(stmt, 0));
- pt->subu_username = strdup(sqlite3_column_text(stmt, 1));
- rc = sqlite3_step(stmt);
- pt++;
- }
- sqlite3_finalize(stmt);
- if( rc != SQLITE_DONE ) return rc;
- return SQLITE_OK;
-}
-
-//--------------------------------------------------------------------------------
-int subudb_Masteru_Subu_rm(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){
- char *sql = "DELETE FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2 AND subu_username = ?3;";
- size_t sql_len = strlen(sql);
- sqlite3_stmt *stmt;
- int rc;
- rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL);
- if( rc != SQLITE_OK ) return rc;
- sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC);
- sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC);
- sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC);
- rc = sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- if( rc == SQLITE_DONE ) return SQLITE_OK;
- return rc;
-}
+++ /dev/null
-/*
-mount all the subu user directories into master's subuland
-uses unmount to undo this
-
-*/
-#include "subu-bind-all.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
- if( argc != 1){
- fprintf(stderr, "%s does not take arguments\n",argv[0]);
- return SUBU_ERR_ARG_CNT;
- }
-
- int rc;
- sqlite3 *db;
- rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
- return SUBU_ERR_DB_FILE;
- }
-
- char *mess;
- rc = subu_bind_all(&mess, db);
- if(rc != 0){
- fprintf(stderr, "subu-bind: %s\n", mess);
- return rc;
- }
- return 0;
-}
--- /dev/null
+#tranche subu-bind-all.cli.c
+/*
+mount all the subu user directories into master's subuland
+uses unmount to undo this
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+ if( argc != 1){
+ fprintf(stderr, "%s does not take arguments\n",argv[0]);
+ return SUBU_ERR_ARG_CNT;
+ }
+
+ int rc;
+ sqlite3 *db;
+ rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }
+
+ char *mess;
+ rc = subu_bind_all(&mess, db);
+ if(rc != 0){
+ fprintf(stderr, "subu-bind: %s\n", mess);
+ return rc;
+ }
+ return 0;
+}
+#tranche-end
+++ /dev/null
-/*
-mount a subu user directory into master's subuland
-uses unmount to undo this
-
-*/
-#include "subu-bind.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
-
- if( argc != 4){
- fprintf(stderr, "usage: %s masteru subu_username subuhome\n",argv[0]);
- return SUBU_ERR_ARG_CNT;
- }
-
- int rc;
- char *mess;
- rc = subu_bind(&mess, argv[1], argv[2], argv[3]);
- if(rc != 0){
- fprintf(stderr, "subu-bind: %s\n", mess);
- return rc;
- }
- return 0;
-}
--- /dev/null
+#tranche subu-bind.cli.c
+/*
+mount a subu user directory into master's subuland
+uses unmount to undo this
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+
+ if( argc != 4){
+ fprintf(stderr, "usage: %s masteru subu_username subuhome\n",argv[0]);
+ return SUBU_ERR_ARG_CNT;
+ }
+
+ int rc;
+ char *mess;
+ rc = subu_bind(&mess, argv[1], argv[2], argv[3]);
+ if(rc != 0){
+ fprintf(stderr, "subu-bind: %s\n", mess);
+ return rc;
+ }
+ return 0;
+}
+#tranche-end
+++ /dev/null
-/*
- subu-mk-0 command
-
-*/
-#include "subu-mk-0.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
- char *command = argv[0];
- if( argc != 2 ){
- fprintf(stderr, "usage: %s subu", command);
- return SUBU_ERR_ARG_CNT;
- }
- char *subuname = argv[1];
-
- int rc;
- sqlite3 *db;
- rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "error when opening db, %s\n", DB_File);
- fprintf(stderr, "sqlite3 says: %s\n", sqlite3_errmsg(db));
- sqlite3_close(db);
- return SUBU_ERR_DB_FILE;
- }
-
- char *mess;
- rc = subu_mk_0(&mess, db, subuname);
- if( rc ){
- subu_err("subu_mk_0", rc, mess);
- free(mess);
- sqlite3_close(db);
- return rc;
- }
-
- rc = sqlite3_close(db);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- return 0;
-
-}
--- /dev/null
+#tranche subu-mk-0.cli.c
+/*
+ subu-mk-0 command
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+ char *command = argv[0];
+ if( argc != 2 ){
+ fprintf(stderr, "usage: %s subu", command);
+ return SUBU_ERR_ARG_CNT;
+ }
+ char *subuname = argv[1];
+
+ int rc;
+ sqlite3 *db;
+ rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "error when opening db, %s\n", DB_File);
+ fprintf(stderr, "sqlite3 says: %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return SUBU_ERR_DB_FILE;
+ }
+
+ char *mess;
+ rc = subu_mk_0(&mess, db, subuname);
+ if( rc ){
+ subu_err("subu_mk_0", rc, mess);
+ free(mess);
+ sqlite3_close(db);
+ return rc;
+ }
+
+ rc = sqlite3_close(db);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+
+}
+#tranche-end
+++ /dev/null
-/*
- subu-mk-0 command
-
-*/
-#include "subu-rm-0.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
- char *command = argv[0];
- if( argc != 2 ){
- fprintf(stderr, "usage: %s subu", command);
- return SUBU_ERR_ARG_CNT;
- }
- char *subuname = argv[1];
-
- sqlite3 *db;
- {
- int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File);
- return SUBU_ERR_DB_FILE;
- }}
-
- {
- char *mess=0;
- int ret = subu_rm_0(&mess, db, subuname);
- subu_err("subu_rm_0", ret, mess);
- free(mess);
- return ret;
- }
-}
--- /dev/null
+#tranche subu-rm-0.cli.c
+/*
+ subu-mk-0 command
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+ char *command = argv[0];
+ if( argc != 2 ){
+ fprintf(stderr, "usage: %s subu", command);
+ return SUBU_ERR_ARG_CNT;
+ }
+ char *subuname = argv[1];
+
+ sqlite3 *db;
+ {
+ int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }}
+
+ {
+ char *mess=0;
+ int ret = subu_rm_0(&mess, db, subuname);
+ subu_err("subu_rm_0", ret, mess);
+ free(mess);
+ return ret;
+ }
+}
+#tranche-end
+++ /dev/null
-/*
- sqllite3 is used to maintain the db file, which is currently compiled
- in as /etc/subu.db, (or just subu.db for testing).
-
- masteru is the user who ran this script. The masteru name comes from getuid
- and /etc/passwd.
-
- subu is a subservient user. The subuname is passed in as an argument.
-
- subu-mk-0 synthesizes a new user user name s<number>, calls useradd to creat
- the new uswer account, and enters the relationship between masteru, subu, and
- s<number> in the db file. It is this relation in the db file that
- associates the subuname with the s<number> user.
-
- subu-rm-0 uses userdel to delete the related s<number> user account. It
- then removes the relaton from the db file.
-
- subu-mk-0 and subu-rm-0 are setuid root scripts.
-
-*/
-#include "subu.lib.h"
-
-// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’
-#define _GNU_SOURCE
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#if INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-#endif
-
-//--------------------------------------------------------------------------------
-// dispatched command errors .. should add mkdir and rmdir ...
-//
-char *useradd_mess(int err){
- if(err <= 0) return NULL;
- char *mess;
- switch(err){
- case 1: mess = "can't update password file"; break;
- case 2: mess = "invalid command syntax"; break;
- case 3: mess = "invalid argument to option"; break;
- case 4: mess = "UID already in use (and no -o)"; break;
- case 5: mess = "undefined"; break;
- case 6: mess = "specified group doesn't exist"; break;
- case 7:
- case 8: mess = "undefined"; break;
- case 9: mess = "username already in use"; break;
- case 10: mess = "can't update group file:"; break;
- case 11: mess = "undefined"; break;
- case 12: mess = "can't create home directory"; break;
- case 13: mess = "undefined"; break;
- case 14: mess = "can't update SELinux user mapping"; break;
- default: mess = "undefined"; break;
- }
- return strdup(mess);
-}
-char *userdel_mess(int err){
- if(err <= 0) return NULL;
- char *mess;
- switch(err){
- case 1: mess = "can't update password file"; break;
- case 2: mess = "invalid command syntax"; break;
- case 3:
- case 4:
- case 5: mess = "undefined"; break;
- case 6: mess = "specified user doesn't exist"; break;
- case 7:
- case 8:
- case 9: mess = "undefined"; break;
- case 10: mess = "can't update group file:"; break;
- case 11: mess = "undefined"; break;
- case 12: mess = "can't remove home directory"; break;
- default: mess = "undefined"; break;
- }
- return strdup(mess);
-}
-
-
-//--------------------------------------------------------------------------------
-//
-#if INTERFACE
-#define SUBU_ERR_ARG_CNT 1
-#define SUBU_ERR_SETUID_ROOT 2
-#define SUBU_ERR_MALLOC 3
-#define SUBU_ERR_MKDIR_SUBUHOME 4
-#define SUBU_ERR_RMDIR_SUBUHOME 5
-#define SUBU_ERR_SUBUNAME_MALFORMED 6
-#define SUBU_ERR_HOMELESS 7
-#define SUBU_ERR_DB_FILE 8
-#define SUBU_ERR_SUBUHOME_EXISTS 9
-#define SUBU_ERR_BUG_SSS 10
-#define SUBU_ERR_FAILED_USERADD 11
-#define SUBU_ERR_FAILED_USERDEL 12
-#define SUBU_ERR_SUBU_NOT_FOUND 13
-#define SUBU_ERR_N 14
-#define SUBU_ERR_BIND 15
-#endif
-
-void subu_err(char *fname, int err, char *mess){
- if(!mess) mess = "";
- switch(err){
- case 0: return;
- case SUBU_ERR_ARG_CNT:
- if(mess[0])
- fprintf(stderr, "Incorrect number of arguments, %s", mess);
- else
- fprintf(stderr, "Incorrect number of arguments.", mess);
- break;
- case SUBU_ERR_SETUID_ROOT:
- fprintf(stderr, "This program must be run setuid root from a user account.");
- break;
- case SUBU_ERR_MALLOC:
- perror(fname);
- break;
- case SUBU_ERR_DB_FILE:
- fprintf(stderr, "error on %s", DB_File); // DB_File is in common
- fprintf(stderr, ": %s", mess);
- break;
- case SUBU_ERR_HOMELESS:
- fprintf(stderr,"Masteru, \"%s\", has no home directory", mess);
- break;
- case SUBU_ERR_SUBUNAME_MALFORMED:
- fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", mess);
- break;
- case SUBU_ERR_SUBUHOME_EXISTS:
- fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", mess);
- break;
- case SUBU_ERR_MKDIR_SUBUHOME:
- fprintf(stderr, "masteru could not make subuhome, \"%s\"", mess);
- break;
- case SUBU_ERR_BUG_SSS:
- perror(fname);
- break;
- case SUBU_ERR_FAILED_USERADD:
- fprintf(stderr, "%s useradd failed\n", mess);
- break;
- default:
- fprintf(stderr, "unknown error code %d\n", err);
- }
- fputc('\n', stderr);
-}
-
-
-//--------------------------------------------------------------------------------
-// a well formed subuname
-// returns the length of the subuname, or -1
-//
-static int allowed_subuname(char **mess, char *subuname, size_t *subuname_len){
- char *ch = subuname;
- size_t i = 0;
- while(
- *ch
- &&
- ( *ch >= 'a' && *ch <= 'z'
- ||
- *ch >= 'A' && *ch <= 'Z'
- ||
- *ch >= '0' && *ch <= '9'
- ||
- *ch == '-'
- ||
- *ch == '_'
- ||
- *ch == '.'
- ||
- *ch == ' '
- )
- ){
- ch++;
- i++;
- }
- if( !*ch ){
- *subuname_len = i;
- return 0;
- }else{
- if(mess) *mess = strdup(subuname);
- return SUBU_ERR_SUBUNAME_MALFORMED;
- }
-}
-
-//--------------------------------------------------------------------------------
-// dispatched functions
-//
-static int masteru_mkdir_subuhome(void *arg){
- char *subuhome = (char *) arg;
- if( mkdir( subuhome, Subuhome_Perms ) == -1 ){ // find subuhome perms in common
- perror("masteru_mkdir_subuhome");
- return SUBU_ERR_MKDIR_SUBUHOME;
- }
- return 0;
-}
-static int masteru_rmdir_subuhome(void *arg){
- char *subuhome = (char *) arg;
- if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common
- perror("masteru_rmdir_subuhome");
- return SUBU_ERR_RMDIR_SUBUHOME;
- }
- return 0;
-}
-
-//--------------------------------------------------------------------------------
-// build strings
-//
-static int mk_subu_username(char **mess, sqlite3 *db, char **subu_username){
- int rc,n;
- db_begin(db);
- if(
- (rc = subudb_number_get(db, &n))
- ||
- (rc = subudb_number_set(db, ++n))
- ){
- db_rollback(db);
- return SUBU_ERR_DB_FILE;
- }
- db_commit(db);
-
- size_t len = 0;
- FILE* name_stream = open_memstream(subu_username, &len);
- fprintf(name_stream, "s%d", n);
- fclose(name_stream);
- return 0;
-}
-
-// man page says that getpwuid strings may not be freed, I don't know how long until they
-// are overwritten, so I just make my own copies that can be freed
-static int uid_to_name_and_home(uid_t uid, char **name, char **home ){
- struct passwd *pw_record_pt = getpwuid(uid); // reading /etc/passwd
- *name = strdup(pw_record_pt->pw_name);
- *home = strdup(pw_record_pt->pw_dir);
- if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS;
- return 0;
-}
-
-static int username_to_home(char *name, char **home ){
- struct passwd *pw_record_pt = getpwnam(name); // reading /etc/passwd
- *home = strdup(pw_record_pt->pw_dir);
- if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS;
- return 0;
-}
-
-static int mk_subuland(char *masteru_home, char **subuland){
- size_t masteru_home_len = strlen(masteru_home);
- char *Subuland_Extension = "/subuland/";
- size_t Subuland_Extension_len = strlen(Subuland_Extension);
- *subuland = (char *)malloc( masteru_home_len + Subuland_Extension_len + 1 );
- if(!*subuland) SUBU_ERR_MALLOC;
- strcpy(*subuland, masteru_home);
- strcpy(*subuland + masteru_home_len, Subuland_Extension);
- return 0;
-}
-
-static int mk_subuhome(char *subuland, char *subuname, char **subuhome){
- size_t subuland_len = strlen(subuland);
- size_t subuhome_len = subuland_len + strlen(subuname); // subuland has a trailing '/'
- *subuhome = (char *)malloc(subuhome_len + 1);
- if(!*subuhome) return SUBU_ERR_MALLOC;
- strcpy (*subuhome, subuland);
- strcpy (*subuhome + subuland_len, subuname);
- return 0;
-}
-
-
-
-//===============================================================================
-int subu_mk_0(char **mess, sqlite3 *db, char *subuname){
-
- int rc;
- if(mess)*mess = 0;
- da resources;
- da_alloc(&resources, sizeof(char *));
-
- //--------------------------------------------------------------------------------
- size_t subuname_len;
- rc = allowed_subuname(mess, subuname, &subuname_len);
- if(rc) return rc;
- #ifdef DEBUG
- debug_printf("subuname is well formed\n");
- #endif
-
- //--------------------------------------------------------------------------------
- uid_t masteru_uid;
- gid_t masteru_gid;
- uid_t set_euid;
- gid_t set_egid;
- {
- masteru_uid = getuid();
- masteru_gid = getgid();
- set_euid = geteuid();
- set_egid = getegid();
- #ifdef DEBUG
- debug_printf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid);
- #endif
- if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT;
- }
-
- //--------------------------------------------------------------------------------
- char *masteru_name = 0;
- char *masteru_home = 0;
- char *subu_username = 0;
- char *subuland = 0;
- char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir
- da_push(&resources, masteru_name);
- da_push(&resources, masteru_home);
- da_push(&resources, subu_username);
- da_push(&resources, subuland);
- da_push(&resources, subuhome);
- rc =
- uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home)
- ||
- mk_subu_username(mess, db, &subu_username)
- ||
- mk_subuland(masteru_home, &subuland)
- ||
- mk_subuhome(subuland, subuname, &subuhome)
- ;
- if(rc) RETURN(&resources, rc);
- #ifdef DEBUG
- debug_printf("subu_username, subuland, subuhome: \"%s\"\"%s\"\"%s\"\n", subu_username, subuland, subuhome);
- #endif
-
- //--------------------------------------------------------------------------------
- // By having masteru create the subuhome, we know that masteru has rights to
- // to access this directory. This will be the mount point for bindfs
- {
- struct stat st;
- if( stat(subuhome, &st) != -1 ){
- if(mess)*mess = strdup(subuhome);
- RETURN(&resources, SUBU_ERR_SUBUHOME_EXISTS);
- }
- int dispatch_err = dispatch_f_euid_egid
- (
- "masteru_mkdir_subuhome",
- masteru_mkdir_subuhome,
- (void *)subuhome,
- masteru_uid,
- masteru_gid
- );
- if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_MKDIR_SUBUHOME ){
- #ifdef DEBUG
- dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_mkdir_subuhome");
- #endif
- if(mess)*mess = strdup(subuhome);
- RETURN(&resources, SUBU_ERR_MKDIR_SUBUHOME);
- }
- }
- #ifdef DEBUG
- debug_printf("made directory \"%s\"\n", subuhome);
- #endif
-
- //--------------------------------------------------------------------------------
- // Make the subservient user account, i.e. the subu
- {
- #ifdef DEBUG
- debug_printf("making subu \"%s\" as user \"%s\"\n", subuname, subu_username);
- #endif
- #if BUG_SSS_CACHE_RUID
- #ifdef DEBUG
- debug_printf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n");
- #endif
- if( setuid(0) == -1 ) RETURN(&resources, SUBU_ERR_BUG_SSS);
- #endif
- char *command = "/usr/sbin/useradd";
- char *argv[3];
- argv[0] = command;
- argv[1] = subu_username;
- argv[2] = (char *) NULL;
- char *envp[1];
- envp[0] = (char *) NULL;
- int dispatch_err = dispatch_exec(argv, envp);
- if( dispatch_err != 0 ){
- #ifdef DEBUG
- dispatch_f_mess("dispatch_exec", dispatch_err, command);
- #endif
- // go back and remove the directory we made in subuland
- int dispatch_err_rmdir = dispatch_f_euid_egid
- (
- "masteru_rmdir_subuhome",
- masteru_rmdir_subuhome,
- (void *)subuhome,
- masteru_uid,
- masteru_gid
- );
- #ifdef DEBUG
- dispatch_f_mess("dispatch_f_euid_egid", dispatch_err_rmdir, "masteru_rmdir_subuhome");
- #endif
- if(mess)*mess = useradd_mess(dispatch_err);
- RETURN(&resources, SUBU_ERR_FAILED_USERADD);
- }
- #ifdef DEBUG
- debug_printf("added user \"%s\"\n", subu_username);
- #endif
- }
-
- //--------------------------------------------------------------------------------
- #ifdef DEBUG
- debug_printf("setting the masteru_name, subuname, subu_username relation\n");
- #endif
- {
- int rc = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
- if( rc != SQLITE_OK ){
- if(mess)*mess = strdup("insert of masteru subu relation failed");
- RETURN(&resources, SUBU_ERR_DB_FILE);
- }
- }
- #ifdef DEBUG
- debug_printf("finished subu-mk-0(%s)\n", subuname);
- #endif
- RETURN(&resources, 0);
-}
-
-//================================================================================
-int subu_rm_0(char **mess, sqlite3 *db, char *subuname){
-
- int rc;
- if(mess)*mess = 0;
- da resources;
- da_alloc(&resources, sizeof(char *));
-
- //--------------------------------------------------------------------------------
- size_t subuname_len;
- rc = allowed_subuname(mess, subuname, &subuname_len);
- if(rc) return rc;
- #ifdef DEBUG
- debug_printf("subuname is well formed\n");
- #endif
-
- //--------------------------------------------------------------------------------
- uid_t masteru_uid;
- gid_t masteru_gid;
- uid_t set_euid;
- gid_t set_egid;
- {
- masteru_uid = getuid();
- masteru_gid = getgid();
- set_euid = geteuid();
- set_egid = getegid();
- #ifdef DEBUG
- debug_printf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid);
- #endif
- if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT;
- }
-
- //--------------------------------------------------------------------------------
- // various strings that we will need
- char *masteru_name = 0;
- char *masteru_home = 0;
- char *subuland = 0;
- char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir
- da_push(&resources, masteru_name);
- da_push(&resources, masteru_home);
- da_push(&resources, subuland);
- da_push(&resources, subuhome);
- rc =
- uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home)
- ||
- mk_subuland(masteru_home, &subuland)
- ||
- mk_subuhome(subuland, subuname, &subuhome)
- ;
- if(rc) RETURN(&resources, rc);
- #ifdef DEBUG
- debug_printf("masteru_home, subuhome: \"%s\", \"%s\"\n", masteru_home, subuhome);
- #endif
-
- //--------------------------------------------------------------------------------
- // removal from db
- char *subu_username = 0;
- da_push(&resources, subu_username);
-
- db_begin(db);
-
- rc = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username);
- if( rc != SQLITE_OK ){
- if(mess) *mess = strdup("subu requested for removal not found under this masteru in db file");
- rc = SUBU_ERR_SUBU_NOT_FOUND;
- db_rollback(db);
- RETURN(&resources, rc);
- }
- #ifdef DEBUG
- printf("subu_username: \"%s\"\n", subu_username);
- #endif
-
- rc = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
- if( rc != SQLITE_OK ){
- if(mess)*mess = strdup("removal of masteru subu relation failed");
- db_rollback(db);
- RETURN(&resources, SUBU_ERR_DB_FILE);
- }
- #ifdef DEBUG
- debug_printf("removed the masteru_name, subuname, subu_username relation\n");
- #endif
-
- rc = db_commit(db);
- if( rc != SQLITE_OK ){
- if(mess)*mess = strdup("removal of masteru subu relation in unknown state, exiting");
- RETURN(&resources, SUBU_ERR_DB_FILE);
- }
-
- // even after removing the last masteru subu relation, we still do not remove
- // the max subu count. Hence, a masteru will keep such for a life time.
-
-
- //--------------------------------------------------------------------------------
- // Only masteru can remove directories from masteru/subuland, so we switch to
- // masteru's uid to perform the rmdir.
- //
- {
- #ifdef DEBUG
- debug_printf("as masteru, removing the directory \"%s\"\n", subuhome);
- #endif
- int dispatch_err = dispatch_f_euid_egid
- (
- "masteru_rmdir_subuhome",
- masteru_rmdir_subuhome,
- (void *)subuhome,
- masteru_uid,
- masteru_gid
- );
- if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_RMDIR_SUBUHOME ){
- #ifdef DEBUG
- dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_rmdir_subuhome");
- #endif
- if(mess)*mess = strdup(subuhome);
- RETURN(&resources, SUBU_ERR_RMDIR_SUBUHOME);
- }
- }
-
- //--------------------------------------------------------------------------------
- // Delete the subservient user account
- {
- #ifdef DEBUG
- debug_printf("deleting user \"%s\"\n", subu_username);
- #endif
- #if BUG_SSS_CACHE_RUID
- #ifdef DEBUG
- debug_printf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n");
- #endif
- if( setuid(0) == -1 ){
- RETURN(&resources, SUBU_ERR_BUG_SSS);
- }
- #endif
- char *command = "/usr/sbin/userdel";
- char *argv[4];
- argv[0] = command;
- argv[1] = subu_username;
- argv[2] = "-r";
- argv[3] = (char *) NULL;
- char *envp[1];
- envp[0] = (char *) NULL;
- int dispatch_err = dispatch_exec(argv, envp);
- if( dispatch_err != 0 ){
- #ifdef DEBUG
- dispatch_f_mess("dispatch_exec", dispatch_err, command);
- #endif
- if(mess)*mess = userdel_mess(dispatch_err);
- RETURN(&resources, SUBU_ERR_FAILED_USERDEL);
- }
- #ifdef DEBUG
- debug_printf("deleted user \"%s\"\n", subu_username);
- #endif
- }
-
- #ifdef DEBUG
- debug_printf("finished subu-rm-0(%s)\n", subuname);
- #endif
- RETURN(&resources, 0);
-}
-
-//================================================================================
-// identifies masteru, the bindfs maps each subu_user's home to its mount point
-// in subuland.
-int subu_bind(char **mess, char *masteru_name, char *subu_username, char *subuhome){
-
- int rc;
- if(mess)*mess = 0;
- da resources;
- da_alloc(&resources, sizeof(char *));
-
- // lookup the subu_user_home
- char *subu_user_home = 0;
- da_push(&resources, subu_user_home);
-
- rc = username_to_home(subu_username, &subu_user_home);
- if( rc ){
- if(mess) *mess = strdup("in subu_bind, subu user home directory lookup in /etc/passwd failed.");
- RETURN(&resources, rc);
- }
-
- size_t len = 0;
- char *map = 0;
- da_push(&resources, map);
-
- FILE* map_stream = open_memstream(&map, &len);
- fprintf(map_stream, "--map=%s/%s:@%s/@%s", subu_username, masteru_name, subu_username, masteru_name);
- fclose(map_stream);
-
- char *command = "/usr/bin/bindfs";
- char *argv[5];
- argv[0] = command;
- argv[1] = map;
- argv[2] = subu_user_home;
- argv[3] = subuhome;
- argv[4] = (char *) NULL;
- char *envp[1];
- envp[0] = (char *) NULL;
- int dispatch_err = dispatch_exec(argv, envp);
- if( dispatch_err != 0 ){
- #ifdef DEBUG
- dispatch_f_mess(command, dispatch_err, "dispatch_exec");
- #endif
- if(mess)*mess = strdup("bind failed");
- RETURN(&resources, SUBU_ERR_BIND);
- }
- #ifdef DEBUG
- debug_printf("mapped \"%s\" as \"%s\"\n", subu_user_home, subuhome);
- #endif
- RETURN(&resources, 0);
-}
-
-int subu_bind_all(char **mess, sqlite3 *db){
-
- int rc;
- if(mess)*mess = 0;
- da resources;
- da_alloc(&resources, sizeof(char *));
-
- //--------------------------------------------------------------------------------
- uid_t masteru_uid;
- gid_t masteru_gid;
- uid_t set_euid;
- gid_t set_egid;
- {
- masteru_uid = getuid();
- masteru_gid = getgid();
- set_euid = geteuid();
- set_egid = getegid();
- #ifdef DEBUG
- debug_printf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid);
- #endif
- if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT;
- }
-
- //--------------------------------------------------------------------------------
- // various strings that we will need
- char *masteru_name = 0;
- char *masteru_home = 0;
- char *subuland = 0;
- da_push(&resources, masteru_name);
- da_push(&resources, masteru_home);
- da_push(&resources, subuland);
- rc =
- uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home)
- ||
- mk_subuland(masteru_home, &subuland)
- ;
- if(rc) RETURN(&resources, rc);
- #ifdef DEBUG
- if(masteru_name)
- debug_printf("masteru_name: \"%s\"", masteru_name);
- else
- debug_printf("masteru_name unknown");
- if(subuland)
- debug_printf("subuland: \"%s\"", subuland);
- else
- debug_printf("subuland unknown");
- debug_printf("\n");
- #endif
-
- //--------------------------------------------------------------------------------
- da subus;
- rc = subudb_Masteru_Subu_get_subus(db, masteru_name, &subus);
- if( rc != SQLITE_OK ){
- if(mess)*mess = strdup("db access failed when fetching a list of subus");
- return rc;
- }
- // a limitation of our error reporting approach is that we can only
- // return one error, but here is a loop that might generate many
- rc = 0;
- char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir
- uint err_cnt = 0;
- subudb_subu_element *pt = (subudb_subu_element *)(subus.base);
- while( !da_endq(&subus,pt) ){
- rc = mk_subuhome(subuland, pt->subuname, &subuhome);
- #ifdef DEBUG
- if(subuhome)
- debug_printf("subuhome: \"%s\"\n", subuhome);
- else
- debug_printf("subuhome unknown \n");
- #endif
- if(!rc) rc = subu_bind(NULL, masteru_name, pt->subu_username, subuhome);
- if(rc) err_cnt++;
- free(subuhome);
- subuhome=0;
- pt++;
- }
- if(err_cnt==1){
- RETURN(&resources, rc);
- }
- if(err_cnt > 1){
- *mess = strdup("multiple errors occured while binding subus");
- RETURN(&resources, SUBU_ERR_BIND);
- }
- RETURN(&resources, 0);
-}
--- /dev/null
+#tranche subu.lib.c
+/*
+ sqllite3 is used to maintain the db file, which is currently compiled
+ in as /etc/subu.db, (or just subu.db for testing).
+
+ masteru is the user who ran this script. The masteru name comes from getuid
+ and /etc/passwd.
+
+ subu is a subservient user. The subuname is passed in as an argument.
+
+ subu-mk-0 synthesizes a new user user name s<number>, calls useradd to creat
+ the new uswer account, and enters the relationship between masteru, subu, and
+ s<number> in the db file. It is this relation in the db file that
+ associates the subuname with the s<number> user.
+
+ subu-rm-0 uses userdel to delete the related s<number> user account. It
+ then removes the relaton from the db file.
+
+ subu-mk-0 and subu-rm-0 are setuid root scripts.
+
+*/
+#include <da.h>
+#include <debug.h>
+#include <dispatch.h>
+#include "common.lib.h"
+#include "subudb.lib.h"
+#include "subu.lib.h"
+
+// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#tranche subu.lib.h
+ #include <stdbool.h>
+ #include <errno.h>
+ #include <sqlite3.h>
+#tranche-end
+
+//--------------------------------------------------------------------------------
+// dispatched command errors .. should add mkdir and rmdir ...
+//
+#tranche subu.lib.h
+ char *useradd_mess(int err);
+#tranche-end
+char *useradd_mess(int err){
+ if(err <= 0) return NULL;
+ char *mess;
+ switch(err){
+ case 1: mess = "can't update password file"; break;
+ case 2: mess = "invalid command syntax"; break;
+ case 3: mess = "invalid argument to option"; break;
+ case 4: mess = "UID already in use (and no -o)"; break;
+ case 5: mess = "undefined"; break;
+ case 6: mess = "specified group doesn't exist"; break;
+ case 7:
+ case 8: mess = "undefined"; break;
+ case 9: mess = "username already in use"; break;
+ case 10: mess = "can't update group file:"; break;
+ case 11: mess = "undefined"; break;
+ case 12: mess = "can't create home directory"; break;
+ case 13: mess = "undefined"; break;
+ case 14: mess = "can't update SELinux user mapping"; break;
+ default: mess = "undefined"; break;
+ }
+ return strdup(mess);
+}
+#tranche subu.lib.h
+ char *userdel_mess(int err);
+#tranche-end
+char *userdel_mess(int err){
+ if(err <= 0) return NULL;
+ char *mess;
+ switch(err){
+ case 1: mess = "can't update password file"; break;
+ case 2: mess = "invalid command syntax"; break;
+ case 3:
+ case 4:
+ case 5: mess = "undefined"; break;
+ case 6: mess = "specified user doesn't exist"; break;
+ case 7:
+ case 8:
+ case 9: mess = "undefined"; break;
+ case 10: mess = "can't update group file:"; break;
+ case 11: mess = "undefined"; break;
+ case 12: mess = "can't remove home directory"; break;
+ default: mess = "undefined"; break;
+ }
+ return strdup(mess);
+}
+
+
+//--------------------------------------------------------------------------------
+//
+#tranche subu.lib.h
+ #define SUBU_ERR_ARG_CNT 1
+ #define SUBU_ERR_SETUID_ROOT 2
+ #define SUBU_ERR_MALLOC 3
+ #define SUBU_ERR_MKDIR_SUBUHOME 4
+ #define SUBU_ERR_RMDIR_SUBUHOME 5
+ #define SUBU_ERR_SUBUNAME_MALFORMED 6
+ #define SUBU_ERR_HOMELESS 7
+ #define SUBU_ERR_DB_FILE 8
+ #define SUBU_ERR_SUBUHOME_EXISTS 9
+ #define SUBU_ERR_BUG_SSS 10
+ #define SUBU_ERR_FAILED_USERADD 11
+ #define SUBU_ERR_FAILED_USERDEL 12
+ #define SUBU_ERR_SUBU_NOT_FOUND 13
+ #define SUBU_ERR_N 14
+ #define SUBU_ERR_BIND 15
+#tranche-end
+#tranche subu.lib.h
+ void subu_err(char *fname, int err, char *mess);
+#tranche-end
+void subu_err(char *fname, int err, char *mess){
+ if(!mess) mess = "";
+ switch(err){
+ case 0: return;
+ case SUBU_ERR_ARG_CNT:
+ if(mess[0])
+ fprintf(stderr, "Incorrect number of arguments, %s", mess);
+ else
+ fprintf(stderr, "Incorrect number of arguments.", mess);
+ break;
+ case SUBU_ERR_SETUID_ROOT:
+ fprintf(stderr, "This program must be run setuid root from a user account.");
+ break;
+ case SUBU_ERR_MALLOC:
+ perror(fname);
+ break;
+ case SUBU_ERR_DB_FILE:
+ fprintf(stderr, "error on %s", DB_File); // DB_File is in common
+ fprintf(stderr, ": %s", mess);
+ break;
+ case SUBU_ERR_HOMELESS:
+ fprintf(stderr,"Masteru, \"%s\", has no home directory", mess);
+ break;
+ case SUBU_ERR_SUBUNAME_MALFORMED:
+ fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", mess);
+ break;
+ case SUBU_ERR_SUBUHOME_EXISTS:
+ fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", mess);
+ break;
+ case SUBU_ERR_MKDIR_SUBUHOME:
+ fprintf(stderr, "masteru could not make subuhome, \"%s\"", mess);
+ break;
+ case SUBU_ERR_BUG_SSS:
+ perror(fname);
+ break;
+ case SUBU_ERR_FAILED_USERADD:
+ fprintf(stderr, "%s useradd failed\n", mess);
+ break;
+ default:
+ fprintf(stderr, "unknown error code %d\n", err);
+ }
+ fputc('\n', stderr);
+}
+
+
+//--------------------------------------------------------------------------------
+// a well formed subuname
+// returns the length of the subuname, or -1
+//
+static int allowed_subuname(char **mess, char *subuname, size_t *subuname_len){
+ char *ch = subuname;
+ size_t i = 0;
+ while(
+ *ch
+ &&
+ ( *ch >= 'a' && *ch <= 'z'
+ ||
+ *ch >= 'A' && *ch <= 'Z'
+ ||
+ *ch >= '0' && *ch <= '9'
+ ||
+ *ch == '-'
+ ||
+ *ch == '_'
+ ||
+ *ch == '.'
+ ||
+ *ch == ' '
+ )
+ ){
+ ch++;
+ i++;
+ }
+ if( !*ch ){
+ *subuname_len = i;
+ return 0;
+ }else{
+ if(mess) *mess = strdup(subuname);
+ return SUBU_ERR_SUBUNAME_MALFORMED;
+ }
+}
+
+//--------------------------------------------------------------------------------
+// dispatched functions
+//
+static int masteru_mkdir_subuhome(void *arg){
+ char *subuhome = (char *) arg;
+ if( mkdir( subuhome, Subuhome_Perms ) == -1 ){ // find subuhome perms in common
+ perror("masteru_mkdir_subuhome");
+ return SUBU_ERR_MKDIR_SUBUHOME;
+ }
+ return 0;
+}
+static int masteru_rmdir_subuhome(void *arg){
+ char *subuhome = (char *) arg;
+ if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common
+ perror("masteru_rmdir_subuhome");
+ return SUBU_ERR_RMDIR_SUBUHOME;
+ }
+ return 0;
+}
+
+//--------------------------------------------------------------------------------
+// build strings
+//
+static int mk_subu_username(char **mess, sqlite3 *db, char **subu_username){
+ int rc,n;
+ db_begin(db);
+ if(
+ (rc = subudb_number_get(db, &n))
+ ||
+ (rc = subudb_number_set(db, ++n))
+ ){
+ db_rollback(db);
+ return SUBU_ERR_DB_FILE;
+ }
+ db_commit(db);
+
+ size_t len = 0;
+ FILE* name_stream = open_memstream(subu_username, &len);
+ fprintf(name_stream, "s%d", n);
+ fclose(name_stream);
+ return 0;
+}
+
+// man page says that getpwuid strings may not be freed, I don't know how long until they
+// are overwritten, so I just make my own copies that can be freed
+static int uid_to_name_and_home(uid_t uid, char **name, char **home ){
+ struct passwd *pw_record_pt = getpwuid(uid); // reading /etc/passwd
+ *name = strdup(pw_record_pt->pw_name);
+ *home = strdup(pw_record_pt->pw_dir);
+ if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS;
+ return 0;
+}
+
+static int username_to_home(char *name, char **home ){
+ struct passwd *pw_record_pt = getpwnam(name); // reading /etc/passwd
+ *home = strdup(pw_record_pt->pw_dir);
+ if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS;
+ return 0;
+}
+
+static int mk_subuland(char *masteru_home, char **subuland){
+ size_t masteru_home_len = strlen(masteru_home);
+ char *Subuland_Extension = "/subuland/";
+ size_t Subuland_Extension_len = strlen(Subuland_Extension);
+ *subuland = (char *)malloc( masteru_home_len + Subuland_Extension_len + 1 );
+ if(!*subuland) SUBU_ERR_MALLOC;
+ strcpy(*subuland, masteru_home);
+ strcpy(*subuland + masteru_home_len, Subuland_Extension);
+ return 0;
+}
+
+static int mk_subuhome(char *subuland, char *subuname, char **subuhome){
+ size_t subuland_len = strlen(subuland);
+ size_t subuhome_len = subuland_len + strlen(subuname); // subuland has a trailing '/'
+ *subuhome = (char *)malloc(subuhome_len + 1);
+ if(!*subuhome) return SUBU_ERR_MALLOC;
+ strcpy (*subuhome, subuland);
+ strcpy (*subuhome + subuland_len, subuname);
+ return 0;
+}
+
+
+
+//===============================================================================
+#tranche subu.lib.h
+ int subu_mk_0(char **mess, sqlite3 *db, char *subuname);
+#tranche-end
+int subu_mk_0(char **mess, sqlite3 *db, char *subuname){
+
+ int rc;
+ if(mess)*mess = 0;
+ Da resources;
+ da_alloc(&resources, sizeof(char *));
+
+ //--------------------------------------------------------------------------------
+ size_t subuname_len;
+ rc = allowed_subuname(mess, subuname, &subuname_len);
+ if(rc) return rc;
+ #ifdef DEBUG
+ debug_printf("subuname is well formed\n");
+ #endif
+
+ //--------------------------------------------------------------------------------
+ uid_t masteru_uid;
+ gid_t masteru_gid;
+ uid_t set_euid;
+ gid_t set_egid;
+ {
+ masteru_uid = getuid();
+ masteru_gid = getgid();
+ set_euid = geteuid();
+ set_egid = getegid();
+ #ifdef DEBUG
+ debug_printf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid);
+ #endif
+ if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT;
+ }
+
+ //--------------------------------------------------------------------------------
+ char *masteru_name = 0;
+ char *masteru_home = 0;
+ char *subu_username = 0;
+ char *subuland = 0;
+ char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir
+ da_push(&resources, masteru_name);
+ da_push(&resources, masteru_home);
+ da_push(&resources, subu_username);
+ da_push(&resources, subuland);
+ da_push(&resources, subuhome);
+ rc =
+ uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home)
+ ||
+ mk_subu_username(mess, db, &subu_username)
+ ||
+ mk_subuland(masteru_home, &subuland)
+ ||
+ mk_subuhome(subuland, subuname, &subuhome)
+ ;
+ if(rc) RETURN(&resources, rc);
+ #ifdef DEBUG
+ debug_printf("subu_username, subuland, subuhome: \"%s\"\"%s\"\"%s\"\n", subu_username, subuland, subuhome);
+ #endif
+
+ //--------------------------------------------------------------------------------
+ // By having masteru create the subuhome, we know that masteru has rights to
+ // to access this directory. This will be the mount point for bindfs
+ {
+ struct stat st;
+ if( stat(subuhome, &st) != -1 ){
+ if(mess)*mess = strdup(subuhome);
+ RETURN(&resources, SUBU_ERR_SUBUHOME_EXISTS);
+ }
+ int dispatch_err = dispatch_f_euid_egid
+ (
+ "masteru_mkdir_subuhome",
+ masteru_mkdir_subuhome,
+ (void *)subuhome,
+ masteru_uid,
+ masteru_gid
+ );
+ if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_MKDIR_SUBUHOME ){
+ #ifdef DEBUG
+ dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_mkdir_subuhome");
+ #endif
+ if(mess)*mess = strdup(subuhome);
+ RETURN(&resources, SUBU_ERR_MKDIR_SUBUHOME);
+ }
+ }
+ #ifdef DEBUG
+ debug_printf("made directory \"%s\"\n", subuhome);
+ #endif
+
+ //--------------------------------------------------------------------------------
+ // Make the subservient user account, i.e. the subu
+ {
+ #ifdef DEBUG
+ debug_printf("making subu \"%s\" as user \"%s\"\n", subuname, subu_username);
+ #endif
+ #if BUG_SSS_CACHE_RUID
+ #ifdef DEBUG
+ debug_printf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n");
+ #endif
+ if( setuid(0) == -1 ) RETURN(&resources, SUBU_ERR_BUG_SSS);
+ #endif
+ char *command = "/usr/sbin/useradd";
+ char *argv[3];
+ argv[0] = command;
+ argv[1] = subu_username;
+ argv[2] = (char *) NULL;
+ char *envp[1];
+ envp[0] = (char *) NULL;
+ int dispatch_err = dispatch_exec(argv, envp);
+ if( dispatch_err != 0 ){
+ #ifdef DEBUG
+ dispatch_f_mess("dispatch_exec", dispatch_err, command);
+ #endif
+ // go back and remove the directory we made in subuland
+ int dispatch_err_rmdir = dispatch_f_euid_egid
+ (
+ "masteru_rmdir_subuhome",
+ masteru_rmdir_subuhome,
+ (void *)subuhome,
+ masteru_uid,
+ masteru_gid
+ );
+ #ifdef DEBUG
+ dispatch_f_mess("dispatch_f_euid_egid", dispatch_err_rmdir, "masteru_rmdir_subuhome");
+ #endif
+ if(mess)*mess = useradd_mess(dispatch_err);
+ RETURN(&resources, SUBU_ERR_FAILED_USERADD);
+ }
+ #ifdef DEBUG
+ debug_printf("added user \"%s\"\n", subu_username);
+ #endif
+ }
+
+ //--------------------------------------------------------------------------------
+ #ifdef DEBUG
+ debug_printf("setting the masteru_name, subuname, subu_username relation\n");
+ #endif
+ {
+ int rc = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
+ if( rc != SQLITE_OK ){
+ if(mess)*mess = strdup("insert of masteru subu relation failed");
+ RETURN(&resources, SUBU_ERR_DB_FILE);
+ }
+ }
+ #ifdef DEBUG
+ debug_printf("finished subu-mk-0(%s)\n", subuname);
+ #endif
+ RETURN(&resources, 0);
+}
+
+//================================================================================
+#tranche subu.lib.h
+ int subu_rm_0(char **mess, sqlite3 *db, char *subuname);
+#tranche-end
+int subu_rm_0(char **mess, sqlite3 *db, char *subuname){
+
+ int rc;
+ if(mess)*mess = 0;
+ Da resources;
+ da_alloc(&resources, sizeof(char *));
+
+ //--------------------------------------------------------------------------------
+ size_t subuname_len;
+ rc = allowed_subuname(mess, subuname, &subuname_len);
+ if(rc) return rc;
+ #ifdef DEBUG
+ debug_printf("subuname is well formed\n");
+ #endif
+
+ //--------------------------------------------------------------------------------
+ uid_t masteru_uid;
+ gid_t masteru_gid;
+ uid_t set_euid;
+ gid_t set_egid;
+ {
+ masteru_uid = getuid();
+ masteru_gid = getgid();
+ set_euid = geteuid();
+ set_egid = getegid();
+ #ifdef DEBUG
+ debug_printf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid);
+ #endif
+ if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT;
+ }
+
+ //--------------------------------------------------------------------------------
+ // various strings that we will need
+ char *masteru_name = 0;
+ char *masteru_home = 0;
+ char *subuland = 0;
+ char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir
+ da_push(&resources, masteru_name);
+ da_push(&resources, masteru_home);
+ da_push(&resources, subuland);
+ da_push(&resources, subuhome);
+ rc =
+ uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home)
+ ||
+ mk_subuland(masteru_home, &subuland)
+ ||
+ mk_subuhome(subuland, subuname, &subuhome)
+ ;
+ if(rc) RETURN(&resources, rc);
+ #ifdef DEBUG
+ debug_printf("masteru_home, subuhome: \"%s\", \"%s\"\n", masteru_home, subuhome);
+ #endif
+
+ //--------------------------------------------------------------------------------
+ // removal from db
+ char *subu_username = 0;
+ da_push(&resources, subu_username);
+
+ db_begin(db);
+
+ rc = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username);
+ if( rc != SQLITE_OK ){
+ if(mess) *mess = strdup("subu requested for removal not found under this masteru in db file");
+ rc = SUBU_ERR_SUBU_NOT_FOUND;
+ db_rollback(db);
+ RETURN(&resources, rc);
+ }
+ #ifdef DEBUG
+ printf("subu_username: \"%s\"\n", subu_username);
+ #endif
+
+ rc = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
+ if( rc != SQLITE_OK ){
+ if(mess)*mess = strdup("removal of masteru subu relation failed");
+ db_rollback(db);
+ RETURN(&resources, SUBU_ERR_DB_FILE);
+ }
+ #ifdef DEBUG
+ debug_printf("removed the masteru_name, subuname, subu_username relation\n");
+ #endif
+
+ rc = db_commit(db);
+ if( rc != SQLITE_OK ){
+ if(mess)*mess = strdup("removal of masteru subu relation in unknown state, exiting");
+ RETURN(&resources, SUBU_ERR_DB_FILE);
+ }
+
+ // even after removing the last masteru subu relation, we still do not remove
+ // the max subu count. Hence, a masteru will keep such for a life time.
+
+
+ //--------------------------------------------------------------------------------
+ // Only masteru can remove directories from masteru/subuland, so we switch to
+ // masteru's uid to perform the rmdir.
+ //
+ {
+ #ifdef DEBUG
+ debug_printf("as masteru, removing the directory \"%s\"\n", subuhome);
+ #endif
+ int dispatch_err = dispatch_f_euid_egid
+ (
+ "masteru_rmdir_subuhome",
+ masteru_rmdir_subuhome,
+ (void *)subuhome,
+ masteru_uid,
+ masteru_gid
+ );
+ if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_RMDIR_SUBUHOME ){
+ #ifdef DEBUG
+ dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_rmdir_subuhome");
+ #endif
+ if(mess)*mess = strdup(subuhome);
+ RETURN(&resources, SUBU_ERR_RMDIR_SUBUHOME);
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+ // Delete the subservient user account
+ {
+ #ifdef DEBUG
+ debug_printf("deleting user \"%s\"\n", subu_username);
+ #endif
+ #if BUG_SSS_CACHE_RUID
+ #ifdef DEBUG
+ debug_printf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n");
+ #endif
+ if( setuid(0) == -1 ){
+ RETURN(&resources, SUBU_ERR_BUG_SSS);
+ }
+ #endif
+ char *command = "/usr/sbin/userdel";
+ char *argv[4];
+ argv[0] = command;
+ argv[1] = subu_username;
+ argv[2] = "-r";
+ argv[3] = (char *) NULL;
+ char *envp[1];
+ envp[0] = (char *) NULL;
+ int dispatch_err = dispatch_exec(argv, envp);
+ if( dispatch_err != 0 ){
+ #ifdef DEBUG
+ dispatch_f_mess("dispatch_exec", dispatch_err, command);
+ #endif
+ if(mess)*mess = userdel_mess(dispatch_err);
+ RETURN(&resources, SUBU_ERR_FAILED_USERDEL);
+ }
+ #ifdef DEBUG
+ debug_printf("deleted user \"%s\"\n", subu_username);
+ #endif
+ }
+
+ #ifdef DEBUG
+ debug_printf("finished subu-rm-0(%s)\n", subuname);
+ #endif
+ RETURN(&resources, 0);
+}
+
+//================================================================================
+// identifies masteru, the bindfs maps each subu_user's home to its mount point
+// in subuland.
+#tranche subu.lib.h
+ int subu_bind(char **mess, char *masteru_name, char *subu_username, char *subuhome);
+#tranche-end
+int subu_bind(char **mess, char *masteru_name, char *subu_username, char *subuhome){
+
+ int rc;
+ if(mess)*mess = 0;
+ Da resources;
+ da_alloc(&resources, sizeof(char *));
+
+ // lookup the subu_user_home
+ char *subu_user_home = 0;
+ da_push(&resources, subu_user_home);
+
+ rc = username_to_home(subu_username, &subu_user_home);
+ if( rc ){
+ if(mess) *mess = strdup("in subu_bind, subu user home directory lookup in /etc/passwd failed.");
+ RETURN(&resources, rc);
+ }
+
+ size_t len = 0;
+ char *map = 0;
+ da_push(&resources, map);
+
+ FILE* map_stream = open_memstream(&map, &len);
+ fprintf(map_stream, "--map=%s/%s:@%s/@%s", subu_username, masteru_name, subu_username, masteru_name);
+ fclose(map_stream);
+
+ char *command = "/usr/bin/bindfs";
+ char *argv[5];
+ argv[0] = command;
+ argv[1] = map;
+ argv[2] = subu_user_home;
+ argv[3] = subuhome;
+ argv[4] = (char *) NULL;
+ char *envp[1];
+ envp[0] = (char *) NULL;
+ int dispatch_err = dispatch_exec(argv, envp);
+ if( dispatch_err != 0 ){
+ #ifdef DEBUG
+ dispatch_f_mess(command, dispatch_err, "dispatch_exec");
+ #endif
+ if(mess)*mess = strdup("bind failed");
+ RETURN(&resources, SUBU_ERR_BIND);
+ }
+ #ifdef DEBUG
+ debug_printf("mapped \"%s\" as \"%s\"\n", subu_user_home, subuhome);
+ #endif
+ RETURN(&resources, 0);
+}
+
+#tranche subu.lib.h
+ int subu_bind_all(char **mess, sqlite3 *db);
+#tranche-end
+int subu_bind_all(char **mess, sqlite3 *db){
+
+ int rc;
+ if(mess)*mess = 0;
+ Da resources;
+ da_alloc(&resources, sizeof(char *));
+
+ //--------------------------------------------------------------------------------
+ uid_t masteru_uid;
+ gid_t masteru_gid;
+ uid_t set_euid;
+ gid_t set_egid;
+ {
+ masteru_uid = getuid();
+ masteru_gid = getgid();
+ set_euid = geteuid();
+ set_egid = getegid();
+ #ifdef DEBUG
+ debug_printf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid);
+ #endif
+ if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT;
+ }
+
+ //--------------------------------------------------------------------------------
+ // various strings that we will need
+ char *masteru_name = 0;
+ char *masteru_home = 0;
+ char *subuland = 0;
+ da_push(&resources, masteru_name);
+ da_push(&resources, masteru_home);
+ da_push(&resources, subuland);
+ rc =
+ uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home)
+ ||
+ mk_subuland(masteru_home, &subuland)
+ ;
+ if(rc) RETURN(&resources, rc);
+ #ifdef DEBUG
+ if(masteru_name)
+ debug_printf("masteru_name: \"%s\"", masteru_name);
+ else
+ debug_printf("masteru_name unknown");
+ if(subuland)
+ debug_printf("subuland: \"%s\"", subuland);
+ else
+ debug_printf("subuland unknown");
+ debug_printf("\n");
+ #endif
+
+ //--------------------------------------------------------------------------------
+ Da subus;
+ Da *subusp = &subus;
+ da_alloc(subusp, sizeof(subudb_subu_element));
+ rc = subudb_Masteru_Subu_get_subus(db, masteru_name, subusp);
+ if( rc != SQLITE_OK ){
+ if(mess)*mess = strdup("db access failed when fetching a list of subus");
+ return rc;
+ }
+ // a limitation of our error reporting approach is that we can only
+ // return one error, but here is a loop that might generate many
+ rc = 0;
+ char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir
+ uint err_cnt = 0;
+ subudb_subu_element *pt = (subudb_subu_element *)(subusp->base);
+ while( !da_endq(subusp,pt) ){
+ rc = mk_subuhome(subuland, pt->subuname, &subuhome);
+ #ifdef DEBUG
+ if(subuhome)
+ debug_printf("subuhome: \"%s\"\n", subuhome);
+ else
+ debug_printf("subuhome unknown \n");
+ #endif
+ if(!rc) rc = subu_bind(NULL, masteru_name, pt->subu_username, subuhome);
+ if(rc) err_cnt++;
+ free(subuhome);
+ subuhome=0;
+ pt++;
+ }
+ if(err_cnt==1){
+ RETURN(&resources, rc);
+ }
+ if(err_cnt > 1){
+ *mess = strdup("multiple errors occured while binding subus");
+ RETURN(&resources, SUBU_ERR_BIND);
+ }
+ RETURN(&resources, 0);
+}
+#tranche-end
+++ /dev/null
-/*
-This command initializes the db file.
-
-*/
-#include "subudb-init.cli.h"
-#include <stdio.h>
-
-int main(){
- sqlite3 *db;
- if( sqlite3_open(DB_File, &db) != SQLITE_OK ){
- fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File);
- return SUBU_ERR_DB_FILE;
- }
- db_begin(db);
- if( subudb_schema(db) != SQLITE_OK ){
- db_rollback(db);
- fprintf(stderr, "error exit, opened db file but could not build schema\n");
- return SUBU_ERR_DB_FILE;
- }
- db_commit(db);
- if( sqlite3_close(db) != SQLITE_OK ){
- fprintf(stderr, "error exit, could not close the db\n");
- return SUBU_ERR_DB_FILE;
- }
- return 0;
-}
--- /dev/null
+#tranche subudb-init.cli.c
+/*
+This command initializes the db file.
+
+*/
+#include <stdio.h>
+
+int main(){
+ sqlite3 *db;
+ if( sqlite3_open(DB_File, &db) != SQLITE_OK ){
+ fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }
+ db_begin(db);
+ if( subudb_schema(db) != SQLITE_OK ){
+ db_rollback(db);
+ fprintf(stderr, "error exit, opened db file but could not build schema\n");
+ return SUBU_ERR_DB_FILE;
+ }
+ db_commit(db);
+ if( sqlite3_close(db) != SQLITE_OK ){
+ fprintf(stderr, "error exit, could not close the db\n");
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
+#tranche-end
+++ /dev/null
-/*
-Set or get a new maximum subu number. Currently doesn't do the setting part.
-
-*/
-#include "subudb-number.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-int main(int argc, char **argv){
-
- if( argc < 1 || argc > 2){
- fprintf(stderr, "usage: %s [n]\n",argv[0]);
- return SUBU_ERR_ARG_CNT;
- }
-
- int rc;
- sqlite3 *db;
- rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "error exit, could not open db file\n");
- sqlite3_close(db);
- return SUBU_ERR_DB_FILE;
- }
-
- // then arg[1] holds a number to set the max to
- if(argc == 2){
- long int i = strtol(argv[1], NULL, 10);
- if( i < 0 ){
- fprintf(stderr, "n must be positive\n");
- sqlite3_close(db);
- return SUBU_ERR_N;
- }
- if( i > INT_MAX ){
- fprintf(stderr, "n is too big, max supported by this program is %d\n", INT_MAX);
- sqlite3_close(db);
- return SUBU_ERR_N;
- }
- rc = subudb_number_set(db, i);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "couldn't set Max_Subunumber: %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_N;
- }
- }
-
- // read and print the current max
- int n;
- rc = subudb_number_get(db, &n);
- if( rc == SQLITE_OK ){
- printf("%d\n", n);
- }else{
- fprintf(stderr, "couldn't get Max_Subunumber: %s\n", sqlite3_errmsg(db));
- sqlite3_close(db);
- return SUBU_ERR_DB_FILE;
- }
- rc = sqlite3_close(db);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- return 0;
-
-}
--- /dev/null
+#tranche subudb-number.cli.c
+/*
+Set or get a new maximum subu number. Currently doesn't do the setting part.
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+int main(int argc, char **argv){
+
+ if( argc < 1 || argc > 2){
+ fprintf(stderr, "usage: %s [n]\n",argv[0]);
+ return SUBU_ERR_ARG_CNT;
+ }
+
+ int rc;
+ sqlite3 *db;
+ rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "error exit, could not open db file\n");
+ sqlite3_close(db);
+ return SUBU_ERR_DB_FILE;
+ }
+
+ // then arg[1] holds a number to set the max to
+ if(argc == 2){
+ long int i = strtol(argv[1], NULL, 10);
+ if( i < 0 ){
+ fprintf(stderr, "n must be positive\n");
+ sqlite3_close(db);
+ return SUBU_ERR_N;
+ }
+ if( i > INT_MAX ){
+ fprintf(stderr, "n is too big, max supported by this program is %d\n", INT_MAX);
+ sqlite3_close(db);
+ return SUBU_ERR_N;
+ }
+ rc = subudb_number_set(db, i);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "couldn't set Max_Subunumber: %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_N;
+ }
+ }
+
+ // read and print the current max
+ int n;
+ rc = subudb_number_get(db, &n);
+ if( rc == SQLITE_OK ){
+ printf("%d\n", n);
+ }else{
+ fprintf(stderr, "couldn't get Max_Subunumber: %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return SUBU_ERR_DB_FILE;
+ }
+ rc = sqlite3_close(db);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+
+}
+#tranche-end
+++ /dev/null
-/*
-get the username from the db file
-for testing subudb_Masteru_Subu_get_subu_username
-
-*/
-#include "subudb-rel-get.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
-
- if(argc != 3){
- fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]);
- return SUBU_ERR_ARG_CNT;
- }
-
- int rc;
- sqlite3 *db;
- rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
- return SUBU_ERR_DB_FILE;
- }
-
- char *masteru_name = argv[1];
- char *subuname = argv[2];
- char *subu_username;
-
- int ret = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username);
- if( ret != SQLITE_DONE ){
- fprintf(stderr, "subudb_Masteru_Subu_get_subu_username indicates failure by returning %d\n",ret);
- fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- ret = sqlite3_close(db);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
- fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- return 0;
-}
--- /dev/null
+#tranche subudb-rel-get.cli.c
+/*
+get the username from the db file
+for testing subudb_Masteru_Subu_get_subu_username
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+
+ if(argc != 3){
+ fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]);
+ return SUBU_ERR_ARG_CNT;
+ }
+
+ int rc;
+ sqlite3 *db;
+ rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }
+
+ char *masteru_name = argv[1];
+ char *subuname = argv[2];
+ char *subu_username;
+
+ int ret = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username);
+ if( ret != SQLITE_DONE ){
+ fprintf(stderr, "subudb_Masteru_Subu_get_subu_username indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ ret = sqlite3_close(db);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
+#tranche-end
+++ /dev/null
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subudb-rel-put.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
-
- if(argc != 4){
- fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
- return 1;
- }
- char *masteru_name = argv[1];
- char *subuname = argv[2];
- char *subu_username = argv[3];
-
- sqlite3 *db;
- {
- int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
- return SUBU_ERR_DB_FILE;
- }}
-
- int ret = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "subudb_Masteru_Subu_put indicates failure by returning %d\n",ret);
- fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- ret = sqlite3_close(db);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
- fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- return 0;
-}
--- /dev/null
+#tranche subudb-rel-put.cli.c
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+
+ if(argc != 4){
+ fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
+ return 1;
+ }
+ char *masteru_name = argv[1];
+ char *subuname = argv[2];
+ char *subu_username = argv[3];
+
+ sqlite3 *db;
+ {
+ int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }}
+
+ int ret = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "subudb_Masteru_Subu_put indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ ret = sqlite3_close(db);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
+#tranche-end
+++ /dev/null
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subudb-rel-rm.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
-
- if(argc != 4){
- fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
- return 1;
- }
- char *masteru_name = argv[1];
- char *subuname = argv[2];
- char *subu_username = argv[3];
-
- sqlite3 *db;
- {
- int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
- return SUBU_ERR_DB_FILE;
- }}
-
- int ret = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
- if( ret != SQLITE_DONE ){
- fprintf(stderr, "subudb_Masteru_Subu_rm indicates failure by returning %d\n",ret);
- fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
- printf("put failed\n");
- return 2;
- }
- ret = sqlite3_close(db);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
- fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- return 0;
-}
--- /dev/null
+#tranche subudb-rel-rm.cli.c
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+
+ if(argc != 4){
+ fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
+ return 1;
+ }
+ char *masteru_name = argv[1];
+ char *subuname = argv[2];
+ char *subu_username = argv[3];
+
+ sqlite3 *db;
+ {
+ int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }}
+
+ int ret = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
+ if( ret != SQLITE_DONE ){
+ fprintf(stderr, "subudb_Masteru_Subu_rm indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
+ printf("put failed\n");
+ return 2;
+ }
+ ret = sqlite3_close(db);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
+#tranche-end
+++ /dev/null
-/*
-Set or get a new maximum subu number. Currently doesn't do the setting part.
-
-*/
-#include "subudb-subus.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-int main(int argc, char **argv){
-
- if( argc != 2){
- fprintf(stderr, "usage: %s masteru_name\n",argv[0]);
- return SUBU_ERR_ARG_CNT;
- }
- char *masteru_name = argv[1];
-
- int rc;
- sqlite3 *db;
- rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "error exit, could not open db file\n");
- sqlite3_close(db);
- return SUBU_ERR_DB_FILE;
- }
-
- subudb_subu_element *sa;
- subudb_subu_element *sa_end;
- rc = subudb_Masteru_Subu_get_subus(db, masteru_name, &sa, &sa_end);
- if( rc == SQLITE_OK ){
- subudb_subu_element *pt = sa;
- while( pt != sa_end ){
- printf("%s %s\n", pt->subuname, pt->subu_username);
- pt++;
- }
- rc = sqlite3_close(db);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_DB_FILE;
- }
- return 0;
- }
- fprintf(stderr, "lookup failed %s\n", sqlite3_errmsg(db));
- sqlite3_close(db);
- return SUBU_ERR_DB_FILE;
-
-}
--- /dev/null
+#tranche subudb-subus.cli.c
+/*
+Set or get a new maximum subu number. Currently doesn't do the setting part.
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+int main(int argc, char **argv){
+
+ if( argc != 2){
+ fprintf(stderr, "usage: %s masteru_name\n",argv[0]);
+ return SUBU_ERR_ARG_CNT;
+ }
+ char *masteru_name = argv[1];
+
+ int rc;
+ sqlite3 *db;
+ rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "error exit, could not open db file\n");
+ sqlite3_close(db);
+ return SUBU_ERR_DB_FILE;
+ }
+
+ subudb_subu_element *sa;
+ subudb_subu_element *sa_end;
+ rc = subudb_Masteru_Subu_get_subus(db, masteru_name, &sa, &sa_end);
+ if( rc == SQLITE_OK ){
+ subudb_subu_element *pt = sa;
+ while( pt != sa_end ){
+ printf("%s %s\n", pt->subuname, pt->subu_username);
+ pt++;
+ }
+ rc = sqlite3_close(db);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+ }
+ fprintf(stderr, "lookup failed %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return SUBU_ERR_DB_FILE;
+
+}
+#tranche-end
--- /dev/null
+#tranche subudb.lib.c
+#tranche subudb.lib.h
+ /*
+ The db file is maintained in SQLite
+
+ Because linux user names are limited length, subu user names are of a compact
+ form: _s<number>. A separate table translates the numbers into the subu names.
+
+ Each of these returns SQLITE_OK upon success
+ */
+ #include <sqlite3.h>
+#tranche-end
+
+#include <da.h>
+#include "common.lib.h"
+#include "subudb.lib.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+//--------------------------------------------------------------------------------
+// sqlite transactions don't nest. There is a way to use save points, but still
+// we can't just nest transactions. Instead use these wrappers around the whole
+// of something that needs to be in a transaction.
+#tranche subudb.lib.h
+ int db_begin(sqlite3 *db);
+ int db_commit(sqlite3 *db);
+ int db_rollback(sqlite3 *db);
+#tranche-end
+int db_begin(sqlite3 *db){
+ return sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
+}
+int db_commit(sqlite3 *db){
+ return sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
+}
+int db_rollback(sqlite3 *db){
+ return sqlite3_exec(db, "ROLLBACK;", NULL, NULL, NULL);
+}
+
+//--------------------------------------------------------------------------------
+#tranche subudb.lib.h
+ int subudb_schema(sqlite3 *db);
+#tranche-end
+int subudb_schema(sqlite3 *db){
+ int rc;
+
+ { // build tables
+ char sql[] =
+ "CREATE TABLE Masteru_Subu(masteru_name TEXT, subuname TEXT, subu_username TEXT);"
+ "CREATE TABLE Attribute_Int(attribute TEXT, value INT);"
+ ;
+ rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
+ if(rc != SQLITE_OK) return rc;
+ }
+
+ { // data initialization
+ char *sql = "INSERT INTO Attribute_Int (attribute, value) VALUES ('Max_Subunumber', ?1);";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_int(stmt, 1, First_Max_Subunumber);
+ rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ if( rc != SQLITE_DONE ) return rc;
+ }
+
+ return SQLITE_OK;
+}
+
+//--------------------------------------------------------------------------------
+#tranche subudb.lib.h
+ int subudb_number_get(sqlite3 *db, int *n);
+#tranche-end
+int subudb_number_get(sqlite3 *db, int *n){
+ char *sql = "SELECT value FROM Attribute_Int WHERE attribute = 'Max_Subunumber';";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ int rc = sqlite3_step(stmt);
+ if( rc == SQLITE_ROW ){
+ *n = sqlite3_column_int(stmt,0);
+ rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ if( rc != SQLITE_DONE ) return rc;
+ return SQLITE_OK;
+ }
+ // should have a message return, suppose
+ sqlite3_finalize(stmt);
+ return SQLITE_NOTFOUND;
+}
+
+#tranche subudb.lib.h
+ int subudb_number_set(sqlite3 *db, int n);
+#tranche-end
+int subudb_number_set(sqlite3 *db, int n){
+ int rc;
+ char *sql = "UPDATE Attribute_Int SET value = ?1 WHERE attribute = 'Max_Subunumber';";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_int(stmt, 1, n);
+ rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ if( rc == SQLITE_DONE ) return SQLITE_OK;
+ return rc;
+}
+
+//--------------------------------------------------------------------------------
+// put relation into Masteru_Subu table
+#tranche subudb.lib.h
+ int subudb_Masteru_Subu_put(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username);
+#tranche-end
+int subudb_Masteru_Subu_put(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){
+ char *sql = "INSERT INTO Masteru_Subu VALUES (?1, ?2, ?3);";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC);
+ sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC);
+ sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC);
+ int rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ if( rc == SQLITE_DONE ) return SQLITE_OK;
+ return rc;
+}
+
+//--------------------------------------------------------------------------------
+#tranche subudb.lib.h
+ int subudb_Masteru_Subu_get_subu_username(sqlite3 *db, char *masteru_name, char *subuname, char **subu_username);
+#tranche-end
+int subudb_Masteru_Subu_get_subu_username(sqlite3 *db, char *masteru_name, char *subuname, char **subu_username){
+ char *sql = "SELECT subu_username FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2;";
+ size_t sql_len = strlen(sql);
+ sqlite3_stmt *stmt;
+ int rc;
+ rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL);
+ if( rc != SQLITE_OK ) return rc;
+ sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC);
+ sqlite3_bind_text(stmt, 2, subuname, strlen(subuname), SQLITE_STATIC);
+ rc = sqlite3_step(stmt);
+ if( rc == SQLITE_ROW ){
+ const char *username = sqlite3_column_text(stmt, 0);
+ *subu_username = strdup(username);
+ }else{
+ sqlite3_finalize(stmt);
+ return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE
+ }
+ rc = sqlite3_step(stmt);
+ if( rc == SQLITE_DONE ) return SQLITE_OK;
+ return rc;
+}
+
+//--------------------------------------------------------------------------------
+#tranche subudb.lib.h
+ typedef struct{
+ char *subuname; // the name that masteru chose for his or her subu
+ char *subu_username; // the adduser name we gave it, typically of the s<number>
+ } subudb_subu_element;
+ int subudb_Masteru_Subu_get_subus(sqlite3 *db, char *masteru_name, Da *subus);
+#tranche-end
+//returns an array of subudb_subu_elements that correspond to the masteru_name
+int subudb_Masteru_Subu_get_subus(sqlite3 *db, char *masteru_name, Da *subusp){
+ char *sql = "SELECT subuname, subu_username"
+ " FROM Masteru_Subu"
+ " WHERE masteru_name = ?1;";
+ size_t sql_len = strlen(sql);
+ sqlite3_stmt *stmt;
+ int rc;
+ rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL);
+ if( rc != SQLITE_OK ) return rc;
+ sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC);
+
+ subudb_subu_element *pt;
+ rc = sqlite3_step(stmt);
+ while( rc == SQLITE_ROW ){
+ pt = (subudb_subu_element *)da_push_alloc(subusp);
+ pt->subuname = strdup(sqlite3_column_text(stmt, 0));
+ pt->subu_username = strdup(sqlite3_column_text(stmt, 1));
+ rc = sqlite3_step(stmt);
+ }
+ sqlite3_finalize(stmt);
+ if( rc != SQLITE_DONE ) return rc;
+ return SQLITE_OK;
+}
+
+//--------------------------------------------------------------------------------
+#tranche subudb.lib.h
+ int subudb_Masteru_Subu_rm(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username);
+#tranche-end
+int subudb_Masteru_Subu_rm(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){
+ char *sql = "DELETE FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2 AND subu_username = ?3;";
+ size_t sql_len = strlen(sql);
+ sqlite3_stmt *stmt;
+ int rc;
+ rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL);
+ if( rc != SQLITE_OK ) return rc;
+ sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC);
+ sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC);
+ sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC);
+ rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ if( rc == SQLITE_DONE ) return SQLITE_OK;
+ return rc;
+}
--- /dev/null
+
+
+a b c:
+ echo $@
+
+x y z:
+ echo $@
+
+e f g: x y z
+ echo $^
+
--- /dev/null
+#ifndef TRANCHE_LIB_H
+#define TRANCHE_LIB_H
+
+#define TRANCHE_ERR_ARG_PARSE 1
+#define TRANCHE_ERR_SRC_OPEN 2
+#define TRANCHE_ERR_DST_OPEN 4
+#define TRANCHE_ERR_FCLOSE 8
+#define TRANCHE_ERR_HELP 16
+#define TRANCHE_ERR_SNAME 32
+
+void path_trim_slashes(char *path);
+int tranche_send(FILE *src, Da *arg_fds, char *tdir);
+int tranche_target(FILE *src, Da *targets, char *tdir);
+void tranche_make(FILE *src_file, char *src_name, int mfile_fd, char *tdir);
+
+#endif
.PHONY: lib
lib:
- $(MAKE) $@
cp $(SRCDIR)/tranche.lib.h include/tranche.h
+ $(MAKE) $@
.PHONY: exec
exec:
EXECDIR=exec
INCDIR=include
LIBDIR=lib
-SHAREDIR=../share
+SHAREDIR=$(realpath $(PROJECT_SUBU)/module/share)
SRCDIR=src
TESTDIR=
TMPDIR=tmp
#ECHO= echo -e
C=gcc
-CFLAGS=-std=gnu11 -fPIC -I. -I../share/include -ggdb -Werror -DDEBUG -DDEBUGDB
-#CFLAGS=-std=gnu11 -fPIC -I. -I../share/include -Werror
-LINKFLAGS=-Llib -L../share/lib/ -lda -ltranche
+CFLAGS=-std=gnu11 -fPIC -I$(SRCDIR) -I$(INCDIR) -I$(SHAREDIR)/include -ggdb -Werror -DDEBUG -DDEBUGDB
+LINKFLAGS=-L$(LIBDIR) -L$(SHAREDIR)/lib/ -l$(MODULE) -lda
// write a make file rule for making the tranche targets
void tranche_make(FILE *src_file, char *src_name, int mfile_fd, char *tdir){
- // target list
- Da ta;
- Da *tap=&ta; // target array pointer
- da_alloc(tap, sizeof(char *));
- tranche_target(src_file, tap, tdir);
+ // array of the target file names -----------------------------------------
+ Da target_arr;
+ Da *target_arrp=&target_arr; // target array pointer
+ da_alloc(target_arrp, sizeof(char *));
+ tranche_target(src_file, target_arrp, tdir);
+
+ // a space separated list of the target file names
+ Da target_arr_as_string;
+ Da *taasp = &target_arr_as_string;
+ da_alloc(taasp, sizeof(char));
+ char *pt = target_arrp->base; // char * because it points to a byte in the array
+ if( pt < target_arrp->end ){
+ da_string_push(taasp, *(char **)pt);
+ pt += target_arrp->element_size;
+ }
+ while( pt < target_arrp->end ){
+ da_push(taasp, &sp);
+ da_string_push(taasp, *(char **)pt);
+ pt += target_arrp->element_size;
+ }
+ da_free_elements(target_arrp);
+ da_free(target_arrp);
// construct then output the dependency line ----------------------------------------
- Da dla;
- Da *dlap=&dla; // dependency line array pointer
- da_alloc(dlap, sizeof(char));
- char *pt = tap->base; // char * because it points to a byte in the array
- while( pt < tap->end ){
- da_string_push(dlap, *(char **)pt);
- da_push(dlap, &sp);
- pt += tap->element_size;
- }
- da_push(dlap, &colon);
- da_push(dlap, &sp);
- da_string_push(dlap, src_name);
- da_push(dlap, &newline);
- write(mfile_fd, dlap->base, dlap->end - dlap->base);
- da_free_elements(tap);
- da_free(tap);
+ Da make_line_string;
+ Da *mlsp = &make_line_string;
+ da_alloc(mlsp, sizeof(char));
+ da_cat(mlsp, taasp);
+ da_push(mlsp, &colon);
+ da_push(mlsp, &sp);
+ da_string_push(mlsp, src_name);
+ da_push(mlsp, &newline);
+ write(mfile_fd, mlsp->base, mlsp->end - mlsp->base);
// output action lines ----------------------------------------
- da_rewind(dlap); // reuse the line buffer
- da_push(dlap, &tab);
- da_string_push(dlap, "for i in $@; do rm $$i || true; done");
- da_push(dlap, &newline);
- write(mfile_fd, dlap->base, dlap->end - dlap->base);
-
- da_rewind(dlap); // reuse the line buffer
- da_push(dlap, &tab);
- da_string_push(dlap, "tranche $<");
+ da_rewind(mlsp); // reuse make line buffer
+ da_push(mlsp, &tab);
+ da_string_push(mlsp, "rm -f ");
+ da_cat(mlsp, taasp);
+ da_push(mlsp, &newline);
+ write(mfile_fd, mlsp->base, mlsp->end - mlsp->base);
+
+ da_rewind(mlsp); // reuse the line buffer
+ da_push(mlsp, &tab);
+ da_string_push(mlsp, "tranche $<");
if(tdir){
- da_string_push(dlap, " -tdir ");
- da_string_push(dlap, tdir);
+ da_string_push(mlsp, " -tdir ");
+ da_string_push(mlsp, tdir);
}
- da_push(dlap, &newline);
- da_push(dlap, &newline);
- write(mfile_fd, dlap->base, dlap->end - dlap->base);
+ da_push(mlsp, &newline);
+ da_push(mlsp, &newline);
+ write(mfile_fd, mlsp->base, mlsp->end - mlsp->base);
- da_free(dlap);
+ da_free(taasp);
+ da_free(mlsp);
return;
}
+test2.c
+test2.h
\ No newline at end of file
EXECDIR=exec
INCDIR=include
LIBDIR=lib
-SHAREDIR=../share
+SHAREDIR=$(realpath $(PROJECT_SUBU)/module/share)
SRCDIR=src
TESTDIR=test
TMPDIR=tmp
LIBFILE=$(LIBDIR)/lib$(MODULE).a
INCFILE=$(INCDIR)/$(MODULE).h
-
# a single space literal, for example if you wanted to subsitute commas to
# spaces: $(subst $(space),;,$(string))
blank :=
# local customization
# user must define things needed by the local makefile, the compiler, and flags
--include makefile-flags
+include makefile-flags
+-include $(DEPFILE)
#--------------------------------------------------------------------------------
# targets
rm $(LIBFILE) || true
# recipes
--include $(DEPFILE)
-
-$(TMPDIR)/%.o : $(SRCDIR)/%.c
+ $(TMPDIR)/%.o : $(SRCDIR)/%.c
$(C) $(CFLAGS) -o $@ -c $<
-$(TMPDIR)/%.o : $(SRCDIR)/%.cc
+ $(TMPDIR)/%.o : $(SRCDIR)/%.cc
$(CC) $(CCFLAGS) -o $@ -c $<
+