#pragma implementation #include #include #include #include #include #include #include #include #include #include #include #include "accountbatch.h" #include "accountbatch.m" #include #include #include #include #include #include #include "../module_apis/acctextra_api.h" #include #include #include using namespace std; static const char K_ACCOUNTBATCH[]="accountbatch"; static const char K_INDEX[]="index"; static const char K_DATAFILE[]="datafile"; static const char K_DATACMD[]="datacmd"; static const char K_MINGROUP[]="mingroup"; static const char K_GROUPS[]="groups"; static const char K_AUTCMD[]="autcmd"; static const char K_LOGFILE[]="logfile"; static const char K_FIELDPOS[]="fieldpos"; static const char K_FIELDDEFVAL[]="fielddefval"; static const char K_IDPREFIX[]="idprefix"; static const char K_UPDGECOS[]="updgecos"; static const char K_UPDGROUP[]="updgroup"; static const char K_UPDSHELL[]="updshell"; static const char K_UPDHOME[]="updhome"; static const char K_UPDPASSWD[]="updpasswd"; static const char K_UPDALTGR[]="updaltgr"; static const char K_UPDEXTRA[]="updextra"; MODULE_DEFINE_VERSION(accountbatch); static const char *keymenu=NULL; static HELP_FILE help_batch ("accountbatch","batch"); static const char *tb_true[]={"1/0",NULL}; static MESSAGE_DEF msg_accountbatch("accountbatch",tb_true); static const char *tbargs[]={"user","newpassword","islocked","domain",NULL}; static MESSAGE_DEF msg_chgpasswd ("chgpasswd",tbargs); struct PARSE_FIELD{ int fieldpos; // Where to pick the value in the input record SSTRING defval; // Value to use if missing in the input record }; #define BASE_FIELDS 7 // Field known by this module (not extra) #define MAX_EXTRA_FIELDS 100 #define MAX_FIELDS (MAX_EXTRA_FIELDS+BASE_FIELDS) struct PARSE_INFO{ SSTRING idprefix; PARSE_FIELD tb[MAX_FIELDS]; }; static bool showtiming = false; static long long getnow() { struct timeval tv; gettimeofday (&tv,NULL); return tv.tv_sec *(long long)1000000 + tv.tv_usec; } static void showtime (int level, const char *msg, ...) { if (showtiming){ static long long last[10]; long long now = getnow(); if (last[level] != 0){ long long diff = now - last[level]; va_list list; va_start (list,msg); char buf[100]; vsnprintf (buf,sizeof(buf)-1,msg,list); va_end (list); fprintf (stderr,"%*s%s %ld.%06ld\n",level*2,"" ,buf,(long)(diff/1000000),(long)(diff%1000000)); } for (int i=level; i<10; i++) last[i] = now; } } static int account_cnvgroups ( const char *groups, GROUPS &allgroups, set &tbgrp, SSTRING &errors) { SSTRINGS tb; int nb = str_splitline (groups,' ',tb); int ret = nb; for (int i=0; iget(); GROUP *gr = allgroups.getitem (g); if (gr == NULL){ errors.appendf (MSG_U(E_UNKNOWNGROUP,"Unknown group: %s\n"),g); ret = -1; }else{ tbgrp.insert(gr->getgid()); } } return ret; } static int account_cnvgroups ( const char *groups, GROUPS &allgroups, set &tbgrp) { SSTRING errors; int ret = account_cnvgroups (groups,allgroups,tbgrp,errors); if (errors.is_filled()){ xconf_error ("%s\n",errors.get()); } return ret; } /* Verify that all the groups exist. Return -1 if not. */ static int account_checkgroups ( const char *groups, GROUPS &allgroups, SSTRING &errors) { int ret = 0; if (groups[0] != '\0'){ set tbgrp; ret = account_cnvgroups (groups,allgroups,tbgrp,errors); } return ret; } /* Set the password of a user. Send a message so other modules may update the password (samba for one). */ static void account_setpasswd (const char *id, const char *passwd) { const char *tb[]={ id,passwd,"0","/" }; module_sendmessage (msg_chgpasswd,4,tb); SSTRING cmd; cmd.setfromf ("/usr/lib/linuxconf/lib/passwd_chat %s",id); POPEN pop (cmd.get()); if (pop.isok()){ FILE *fout = pop.getfout(); fprintf (fout,"%s\n",passwd); pop.close(); for (int i=0; i<10; i++){ if (pop.wait(1) == -1) break; char err[200]; while (pop.readerr(err,sizeof(err)-1)!=-1){ fprintf (stderr,"passwd_chat: %s\n",err); } } } } #define MAX_COLUMNS 10 #define NOT_SUPPLIED 10 #define DO_MERGE 11 #define PASSWORD_GENERATE 12 /* Select the proper field in the input record or the default value */ static void acct_getfield ( int fieldnum, const PARSE_INFO &parse, const SSTRINGS &tb, SSTRING &val) // Will get the value { int fieldpos = parse.tb[fieldnum].fieldpos; if (fieldpos < MAX_COLUMNS){ SSTRING *s = tb.getitem(fieldpos); if (s != NULL) val.setfrom(s); }else if (fieldpos == NOT_SUPPLIED){ val.setfrom (parse.tb[fieldnum].defval); }else if (fieldpos == DO_MERGE){ const char *fs = parse.tb[fieldnum].defval.get(); while (*fs != '\0'){ char car = *fs++; if (car == '#'){ if (!isdigit(*fs)){ val.append (*fs); fs++; }else{ int no = atoi(fs)-1; SSTRING *s = tb.getitem(no); if (s == NULL){ // val.append("(NULL)"); }else{ val.append (s); } fs = str_skipdig(fs); } }else{ val.append (car); } } } } struct PARSE_RECORD { SSTRING pid; SSTRING group; const char *name; const char *shell; const char *passwd; const char *home; const char *altgr; char generate[10]; const char *extra[MAX_EXTRA_FIELDS]; SSTRING bufs[MAX_FIELDS]; }; static void acct_parse ( const SSTRINGS &tb, // One record to analyse const PARSE_INFO &parse, // How to interpret it PARSE_RECORD &rec, // Will contain the record int nbextra) // Extra fields to parse { for (int i=0; i static void accountbatch_preview ( const char *command, PARSE_INFO &parse) { glocal PARSE_INFO *parse = &parse; glocal const char *command = command; glocal SSTRING_KEYS extra; ACCTEXTRA_API *api = acctextra_api_init("accountbatch"); if (api != NULL){ api->getvars(api,glocal.extra); acctextra_api_end(api); } (MSG_U(T_PREVIEW,"Preview") ,MSG_U(I_PREVIEW,"Here are the first records"),help_nil); SSTRING tmp(MSG_U(H_PREVIEW,"Id\tName\tGroup\tAltgr.\tShell\tHome\tPassword")); for (int i=0; igetobjval()); } newf_head (tmp.get()); sortable(); sortpolicy("aaaaa"); (glocal.command,100); int ret = 0; SSTRINGS tb; SSTRING line2(line); line2.strip_end(); int nb = str_splitline (line2.get(),'\t',tb); if (nb >= 1){ PARSE_RECORD rec; acct_parse (tb,*glocal.parse,rec,glocal.extra.size()); SSTRING tmp; tmp.setfromf ("%s\t%s\t%s\t%s\t%s\t%s" ,rec.name,rec.group.get(),rec.altgr,rec.shell,rec.home,rec.passwd); for (int i=0; i 30) ret = -1; } return ret; } struct ACCT_STATS{ int nbadded; // Will contain the number of account added int nbdeleted; int nbdisabled; int nbupdated; int nbrecord; // How many record processed SSTRING_KEYS passdb; // Record the password generated ACCT_STATS(){ nbadded = nbdeleted = nbdisabled = nbupdated = 0; } }; struct ACCT_TASKS { bool testing; // Do nothing, just count bool silent; // Do not popup any message, used to count // the amount ot account to create/delete bool batchmode; // Print progress on stdout bool addmissing; // Add new account, missing from /etc/passwd bool delold; // Delete old account in /etc/passwd not in datafile bool disold; // Disable old account in /etc/passwd not in datafile bool updatefields; // Update some fields in the existing accounts USER_DELOPER deloper; ACCT_TASKS(){ testing = false; silent = false; batchmode = false; addmissing = false; delold = false; disold = false; updatefields = false; deloper = DELOPER_KEEP; } }; struct ACCT_UPDATES { bool gecos; bool group; bool shell; bool home; bool passwd; bool altgr; bool extra[100]; ACCT_UPDATES(){ gecos = true; group = true; shell = true; home = true; altgr = true; passwd = false; for (unsigned i=0; i static int accountbatch_process ( const ACCT_TASKS &tasks, const ACCT_UPDATES &updates, const ACCT_CONFIG &conf, const char *command, ACCT_STATS &stats, USERS &users, GROUPS &groups) { showtime (1,"start process"); glocal int mingroup = conf.mingroup; glocal ACCTEXTRA_API *api; glocal SSTRING account; glocal SSTRING name; glocal GROUPS *groups = &groups; glocal set tbseen; // Users seen in the datafile glocal SSTRINGS tbdel; glocal SSTRINGS tbdis; glocal SSTRINGS tbadd; glocal SSTRINGS tbupd; glocal set seen_grp; glocal DIALOG dia; glocal SSTRING_KEYS extra; glocal SSTRING_KEYS *passdb = &stats.passdb; glocal const PARSE_INFO *parse = &conf.parse; glocal bool batchmode = tasks.batchmode; glocal bool verbose = tasks.batchmode && !tasks.silent; glocal const ACCT_TASKS *tasks = &tasks; glocal const ACCT_UPDATES *updates = &updates; glocal USERS *users = &users; glocal int added = 0; glocal int deleted = 0; glocal int disabled = 0; glocal int updated = 0; glocal int nbrecord = 0; glocal bool update_extra = false; // Should we update at least // one extra field glocal SSTRING errors; glocal SSTRINGS dups; // Omitted records because they // were dups of previous ones showtime (1,"process init"); if (!tasks.testing && !tasks.silent && !tasks.batchmode){ glocal.dia.settype (DIATYPE_POPUP); glocal.dia.newf_str (MSG_U(F_ACCOUNT,"Account"),glocal.account); glocal.dia.set_lastreadonly(); glocal.dia.newf_str (MSG_U(F_NAME,"Name"),glocal.name); glocal.dia.set_lastreadonly(); if (stats.nbadded > 0){ glocal.dia.newf_gauge (MSG_U(F_ADDED,"Added"),glocal.added ,stats.nbadded); } if (stats.nbdeleted > 0){ glocal.dia.newf_gauge (MSG_U(F_DELETED,"Deleted") ,glocal.deleted,stats.nbdeleted); } if (stats.nbdisabled > 0){ glocal.dia.newf_gauge (MSG_U(F_DISABLED,"Disabled") ,glocal.disabled,stats.nbdisabled); } if (stats.nbupdated > 0){ glocal.dia.newf_gauge (MSG_U(F_UPDATED,"Updated") ,glocal.updated,stats.nbupdated); } acct_dia_reload (glocal.dia); } // Tell other modules we will be creating many accounts // so they want to do some caching static const char *tbon[]={"1",NULL}; module_sendmessage (msg_accountbatch,1,tbon); glocal.api = acctextra_api_init("accountbatch"); if (glocal.api != NULL){ glocal.api->getvars(glocal.api,glocal.extra); for (int i=0; i < glocal.extra.size(); i++){ if (updates.extra[i]) glocal.update_extra = true; } } bool oldshowmode = net_setshowmode (false); showtime (1,"loop users"); { int n = glocal.users->size(); for (int i=0; igetitem(i); glocal.users->getitem(u->getname()); } } showtime (1,"Start walkpopen"); long long proc_mod_start = getnow(); int ret = (command,100); int ret = 0; SSTRING line2(line); line2.strip_end(); SSTRINGS tb; int nb = str_splitline (line2.get(),'\t',tb); if (nb >= 1){ glocal.nbrecord++; PARSE_RECORD rec; acct_parse (tb,*glocal.parse,rec,glocal.extra.size()); const char *id = rec.pid.get(); GROUP *gr; showtime (2,"add/upd id %s",id); SSTRING altgr_errors; if (!isalpha(id[0])){ glocal.errors.appendf (MSG_U(I_IVLDID ,"Line %d: Login %s must start with a letter\n") ,noline,id); }else if ((gr=glocal.groups->getitem(rec.group.get()))==NULL){ glocal.errors.appendf (MSG_U(I_IVLDGRP ,"Line %d: Unknown group %s\n"),noline,rec.group.get()); }else if (account_checkgroups (rec.altgr,*glocal.groups ,altgr_errors)==-1){ glocal.errors.appendf (MSG_U(E_ALTGR,"Line %d: %s\n") ,noline,altgr_errors.get()); }else if (glocal.tbseen.count(id)>0){ SSTRING *s = new SSTRING; s->setfromf (MSG_U(E_DBLENTRY,"Line %d: duplicate entry %s") ,noline,id); glocal.dups.add (s); }else{ glocal.seen_grp.insert (gr->getgid()); glocal.tbseen.insert (id); USER *user = glocal.users->getitem(id); if (user == NULL){ // Ok the user does not exist, we must create it if (glocal.tasks->addmissing){ if (glocal.verbose){ printf (MSG_U(I_ADDING,"Adding account %s %s\n"),id,rec.name); } if (!glocal.tasks->testing){ if (glocal.extra.size() > 0){ SSTRING_KEYS vals; for (int i=0; iget(),rec.extra[i]); } glocal.api->setvals(glocal.api,vals); } glocal.users->addbatch (id,rec.group.get(),rec.name,rec.shell,rec.home,rec.altgr,false); if (rec.passwd[0] != '\0'){ account_setpasswd (id,rec.passwd); if (rec.generate[0] != '\0'){ glocal.passdb->add (rec.pid.get(),rec.passwd); } } glocal.added++; if (!glocal.batchmode){ glocal.account.setfrom(id); glocal.name.setfrom(rec.name); acct_dia_reload (glocal.dia); } } glocal.tbadd.add (new SSTRING(id)); } }else if(glocal.tasks->updatefields){ bool updated = false; bool updated_passwd = false; bool testing = glocal.tasks->testing; SSTRING upd; int oldgid = user->getgid(); GROUP *oldg = glocal.groups->getfromgid(oldgid); upd.setfromf ("%s %s:\n",id,oldg->getname()); if (glocal.updates->passwd){ // Ok, the account exist, we update the password if (rec.passwd[0] != '\0'){ if (!testing){ account_setpasswd (id,rec.passwd); if (rec.generate[0] != '\0'){ glocal.passdb->add (rec.pid.get(),rec.passwd); } } updated_passwd = true; upd.appendf (" %s\n",MSG_U(I_PASSWD,"Password")); } } if (glocal.updates->home){ const char *oldhome = user->gethome(); if (strcmp(oldhome,rec.home)!=0){ upd.appendf (" %s: %s -> %s\n" ,MSG_U(I_HOME,"Home") ,oldhome,rec.home); if (!testing) user->sethome (rec.home); updated = true; } } if (glocal.updates->shell){ const char *oldshell = user->getshell(); if (strcmp(oldshell,rec.shell)!=0){ upd.appendf (" %s: %s -> %s\n" ,MSG_U(I_SHELL,"Shell") ,oldshell,rec.shell); if (!testing) user->setshell (rec.shell); updated = true; } } if (glocal.updates->gecos){ const char *oldgecos = user->getgecos(); if (strcmp(oldgecos,rec.name)!=0){ upd.appendf (" %s: %s -> %s\n" ,MSG_U(I_GECOS,"Gecos") ,oldgecos,rec.name); if (!testing) user->setgecos (rec.name); updated = true; } } SSTRING altgr; glocal.groups->getalt (id,altgr); user->setaltgrs (altgr.get()); if (glocal.updates->altgr){ if (altgr.cmp(rec.altgr)!=0){ upd.appendf (" %s: %s -> %s\n" ,MSG_U(I_ALTGR,"Altgr") ,altgr.get(),rec.altgr); if (!testing) user->setaltgrs (rec.altgr); updated = true; } } if (glocal.updates->group){ int newgid = glocal.groups->getgid(rec.group.get()); if (oldgid != newgid){ upd.appendf (" %s: %s -> %s\n" ,MSG_U(I_GROUP,"Group") ,oldg->getname() ,rec.group.get()); if (!testing) user->setgid (newgid); updated = true; } } if (glocal.update_extra){ SSTRING_KEYS vals; glocal.api->getvals (glocal.api,id,vals); int nbval = vals.size(); for (int i=0; iextra[i]){ SSTRING_KEY *k = vals.getitem(i); const char *newval = rec.extra[i]; if (strcmp(k->getobjval(),newval)!=0){ upd.appendf (" %s: %s -> %s\n" ,k->get(),k->getobjval(),newval); k->setobjval(newval); updated = true; } } } glocal.api->setvals(glocal.api,vals); } if (updated || updated_passwd){ glocal.tbupd.add (upd); glocal.updated++; if (glocal.verbose){ printf (MSG_U(I_UPDATING,"Updating account %s %s\n"),id,rec.name); } if (!testing){ if (updated){ glocal.users->update (user); } if (!glocal.batchmode){ glocal.account.setfrom(id); glocal.name.setfrom(rec.name); acct_dia_reload (glocal.dia); } } } } } }else if (nb != 0){ xconf_error (MSG_U(E_IVLDLINE,"Invalid line %d: %s"),noline,line); ret = -1; } return ret; xconf_error (MSG_U(E_CMDERROR ,"The command %s produced the following error\n" "\t%s"),command,line); return -1; xconf_error (MSG_U(E_NOUSERS,"No user accounts to process")); long long proc_mod_end = getnow(); showtime (1,"End add/upd ret=%d",ret); if (glocal.errors.is_filled()){ ret = -1; xconf_error (MSG_U(E_SOMEERROR ,"There were some errors in the input file\n\n%s\n") ,glocal.errors.get()); } if (glocal.api != NULL) acctextra_api_end(glocal.api); long long proc_del_start = getnow(); if (ret > 0 && (tasks.delold || tasks.disold)){ glocal set tbgrp; if (conf.groups.cmp("*")==0){ glocal.tbgrp = glocal.seen_grp; }else{ account_cnvgroups (conf.groups.get(),*glocal.groups,glocal.tbgrp); } const char *autcmd = conf.autcmd.get(); if (autcmd[0] == '\0'){ autcmd = "/bin/cat"; } (autcmd,100); // To avoid dead lock, we feed the command using a forked // process. // There is another advantage. While we walk glocal.users // the functag oneline remove accounts, modifying the users // array. Because we are forked, we don't care. if (fork()==0){ for (int i=0; isize(); i++){ USER *u = glocal.users->getitem(i); if (glocal.tbseen.count (u->getname())==0){ // Ok, the user does not exist in the list int gid = u->getgid(); const char *id = u->getname(); if ((glocal.mingroup > 0 && gid >= glocal.mingroup)){ // fprintf (stderr,"mingroup: deleting %s\n",u->getname()); fprintf (fout,"%s\n",id); }else if (glocal.tbgrp.count(gid)>0){ // fprintf (stderr,"ingroups: deleting %s\n",u->getname()); fprintf (fout,"%s\n",id); } } } close_input(); _exit(0); } close_input(); USER *u = glocal.users->getitem(line); if (u != NULL){ const char *id = u->getname(); glocal.account = id; glocal.name = u->getgecos(); if (glocal.tasks->delold){ glocal.tbdel.add (new SSTRING(id)); if (glocal.verbose){ printf (MSG_U(I_Deleting,"Deleting account %s\n"),id); } if (!glocal.tasks->testing){ glocal.users->delbatch (id,glocal.tasks->deloper); glocal.deleted++; } }else if (glocal.tasks->disold){ glocal.tbdis.add (new SSTRING(id)); if (glocal.verbose){ printf (MSG_U(I_Disabling,"Disabling account %s\n"),id); } if (!glocal.tasks->testing){ SHADOW *sha = glocal.users->getshadow(u); u->update_passwd (NULL,sha,true,"/"); glocal.users->update(u); glocal.disabled++; } } if (!glocal.tasks->testing && !glocal.batchmode){ acct_dia_reload (glocal.dia); } } return 0; } long long proc_del_end = getnow(); showtime (1,"End delete"); net_setshowmode (oldshowmode); stats.nbrecord = glocal.nbrecord; stats.nbadded = glocal.tbadd.size(); stats.nbdeleted = glocal.tbdel.size(); stats.nbdisabled = glocal.tbdis.size(); stats.nbupdated = glocal.tbupd.size(); static const char *tboff[]={"0",NULL}; module_sendmessage (msg_accountbatch,1,tboff); glocal const char *title; glocal const char *xline; // The following coroutine is used mainly because we // format the report once in the UI and once in a save report (); glocal.xline = glocal.title = NULL; for (int i=0; iget(); yield(); } for (int i=0; iget(); yield(); } for (int i=0; iget(); yield(); } for (int i=0; icnv2lines(lines); for (int j=0; jget(); yield(); } } for (int i=0; iget(); yield(); } glocal COROUTINE *co = &co; glocal SSTRING intro; glocal const char *fname; glocal int retsub=0; (); int ret = -1; ret = (glocal.fname,true); time_t ti = time(NULL); struct tm *t = localtime (&ti); fprintf (fout,"##### %s",asctime(t)); fprintf (fout,"%s\n\n",glocal.intro.get()); glocal.co->restart(); while (glocal.co->next()){ if (glocal.title != NULL){ fprintf (fout,"**** %s\n",glocal.title); glocal.title = NULL; } fprintf (fout,"%s\n",glocal.xline); } return 0; glocal.retsub = ret; glocal OBJECTSUB *sub = ⊂ { long long proc_diff = (proc_mod_end - proc_mod_start) + (proc_del_end - proc_del_start); glocal.intro.setfromf (MSG_U(I_RESULTS ,"%5d records processed\n" "%5d account(s) added\n" "%5d account(s) deleted\n" "%5d account(s) disabled\n" "%5d account(s) modified\n" "%5d duplicate entrie(s)\n" "Processed in %d.%06ld seconds\n") ,stats.nbrecord ,stats.nbadded,stats.nbdeleted,stats.nbdisabled,stats.nbupdated ,glocal.dups.size() ,(long)(proc_diff/1000000) ,(long)(proc_diff%1000000)); } if (!tasks.silent && !tasks.batchmode){ if (stats.nbadded + stats.nbdeleted + stats.nbdisabled + stats.nbupdated + glocal.dups.size() == 0){ xconf_notice (MSG_U(N_DONOTHING,"No account created, deleted, disabled or updated")); }else{ DIALOG info; while (co.next()){ if (glocal.title != NULL){ info.newf_title ("",glocal.title); glocal.title = NULL; } info.newf_info ("",glocal.xline); } int nof = 0; info.settype (DIATYPE_POPUP); info.setbutinfo (MENU_USR1,MSG_U(B_SAVEREP,"Save report"),""); while (true){ MENU_STATUS code = info.edit ( tasks.testing ? MSG_U(T_TEST,"Test results") : MSG_U(T_RESULTS,"Results") ,glocal.intro.get(),help_nil,nof ,MENUBUT_CANCEL|MENUBUT_USR1); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ (MSG_U(T_SAVEREP,"Save report") ,MSG_U(I_SAVEREP ,"Enter the path of a file\n" "where the report will be copied.") ,MSG_U(F_FNAME,"File path"),"",help_nil); glocal.fname = val; glocal.sub->exec(); reject = glocal.retsub == -1; } } } } // Produce the report in the log file as well if (!tasks.testing && stats.nbadded + stats.nbdeleted + stats.nbdisabled + stats.nbupdated != 0 && conf.logfile.is_filled()){ glocal.fname = conf.logfile.get(); sub.exec(); } return ret; } static int acct_savepasswd ( const SSTRING_KEYS &passwords, const char *passfile) { const SSTRING_KEYS *passwords; glocal.passwords = &passwords; int ret = (passfile,false); for (int i=0; igetnb(); i++){ SSTRING_KEY *k = glocal.passwords->getitem(i); fprintf (fout,"%s\t%s\n",k->get(),k->getobjval()); } fchmod (fileno(fout),0600); return 0; return ret; } static int accountbatch_process ( const ACCT_TASKS &tasks, const ACCT_UPDATES &updates, const ACCT_CONFIG &conf, const char *command) { ACCT_STATS stats; // We do a first pass to either test, or count the number of // account to add and delete ACCT_TASKS tmp; tmp.testing = true; tmp.silent = !tasks.testing; tmp.addmissing = tasks.addmissing; tmp.delold = tasks.delold; tmp.disold = tasks.disold; tmp.updatefields = tasks.updatefields; showtime (1,"parent _process"); USERS users; users.readall(); showtime (1,"readall users %d",users.size()); GROUPS groups; showtime (1,"read groups %d",groups.size()); int ret = accountbatch_process (tmp,updates ,conf,command,stats,users,groups); if (ret != -1 && !tasks.testing){ ret = accountbatch_process (tasks,updates,conf,command ,stats,users,groups); if (stats.passdb.getnb()>0){ DIALOG dia; dia.settype (DIATYPE_POPUP); SSTRING path; dia.newf_str (MSG_U(F_LOGPATH,"Password log path"),path); dia.last_noempty(); while (1){ MENU_STATUS code = dia.edit (MSG_U(T_GENERATEDPASSWORD ,"Generated passwords") ,MSG_U(I_GENERATEDPASSWORD ,"Some passwords were generated\n" "You can save those in a file so the user\n" "may be told their password.\n" "Enter the path of the file") ,help_nil); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if(acct_savepasswd (stats.passdb,path.get()) != -1){ break; } } } } return ret; } static void accountbatch_saveconfig( const char *config, ACCT_CONFIG &conf, ACCT_UPDATES &updates) { SSTRINGS tb; linuxconf_getall (K_ACCOUNTBATCH,K_INDEX,tb,true); if (tb.lookup(config)==-1) tb.add (new SSTRING(config)); linuxconf_replace (K_ACCOUNTBATCH,K_INDEX,tb); SSTRING tmp; tmp.setfromf ("%s-%s",K_ACCOUNTBATCH,config); const char *key = tmp.get(); linuxconf_replace (key,K_DATAFILE,conf.datafile); linuxconf_replace (key,K_DATACMD,conf.datacmd); linuxconf_replace (key,K_MINGROUP,conf.mingroup); linuxconf_replace (key,K_GROUPS,conf.groups); linuxconf_replace (key,K_AUTCMD,conf.autcmd); linuxconf_replace (key,K_LOGFILE,conf.logfile); linuxconf_replace (key,K_IDPREFIX,conf.parse.idprefix); for (int i=0; i static void accountbatch_dialog(const char *confname, bool registry_mode) { if (!perm_rootaccess(MSG_U(P_MNGACCOUNT,"manage accounts"))) return; DIALOG dia; ACCT_CONFIG conf; ACCT_UPDATES updates; if (confname != NULL) accountbatch_loadconf (confname,conf,updates); char delmode=0; PRIVATE_MESSAGE config_msg; SSTRING config(confname); dia.auto_newline(true); dia.newf_str (MSG_U(F_CONFIGNAME,"Configuration name"),config); dia.set_helpdia (config_msg); dia.newf_title (MSG_U(I_BASE,"Base info"),1,"",MSG_R(I_BASE)); dia.newf_str (MSG_U(F_DATAFILE,"Data file path"),conf.datafile); dia.newf_str (MSG_U(F_DATACMD,"Data command"),conf.datacmd); static int tbnum[]={0,0}; static const char *tbstr[]={MSG_U(I_NOMINGROUP,"Not specified"),NULL}; int field_groups = dia.getnb(); dia.newf_chkm_num (MSG_U(F_MINGROUP,"Operate on group above"),conf.mingroup, tbnum,tbstr); FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_ONGROUPS ,"Operate only on groups"),conf.groups); comb->addopt ("*",MSG_U(I_STAR,"authoritative on all groups in file")); GROUPS groups; { for (int i=0; iaddopt (g->getname()); } } dia.newf_str (MSG_U(F_CHKCMD,"Authorization command"),conf.autcmd); dia.newf_str (MSG_U(F_LOGFILE,"Log file"),conf.logfile); dia.newf_title (MSG_U(I_PARSING,"Parsing"),1,"",MSG_R(I_PARSING)); dia.auto_newline(false); dia.gui_label (""); dia.gui_label (MSG_U(I_FIELDPOS,"Field pos.")); dia.gui_label (MSG_U(I_DEFVAL,"Default value")); dia.newline(); for (int i=0; iaddopt (tmp); } nm->addopt (MSG_U(I_MISSING,"Not supplied")); nm->addopt (MSG_U(I_MERGE,"Format")); if (i==4) nm->addopt (MSG_U(I_GENERATE,"Generate")); dia.newf_str (NULL,conf.parse.tb[i].defval); dia.newline(); if (i==0){ dia.newf_str (MSG_U(I_IDPREFIX,"Login prefix") ,conf.parse.idprefix,10); dia.newline(); } } ACCTEXTRA_API *api = acctextra_api_init("accountbatch"); SSTRING_KEYS tb; if (api != NULL){ if (api->getvars(api,tb) > 0){ dia.newf_title (MSG_U(I_EXTRAFIELDS,"Extra fields"),1,"",MSG_R(I_EXTRAFIELDS)); dia.gui_label (""); dia.gui_label (MSG_R(I_FIELDPOS)); dia.gui_label (MSG_R(I_DEFVAL)); dia.newline(); for (int i=0; igetobjval(),conf.parse.tb[i+BASE_FIELDS].fieldpos); for (int j=0; jaddopt (tmp); } nm->addopt (MSG_R(I_MISSING)); nm->addopt (MSG_R(I_MERGE)); dia.newf_str (NULL,conf.parse.tb[i+BASE_FIELDS].defval); dia.newline(); } } acctextra_api_end (api); } dia.newf_title (MSG_U(I_TASK,"Task"),1,"",MSG_R(I_TASK)); dia.auto_newline(true); ACCT_TASKS tasks; dia.newf_chk ("",tasks.addmissing,MSG_U(I_ADDMISSING,"Add missing accounts")); dia.newf_chk ("",tasks.updatefields,MSG_U(I_UPDATEPASS,"Update existing accounts")); dia.newf_chk ("",tasks.delold,MSG_U(I_DELOLD,"Delete old accounts")); dia.newf_chk ("",tasks.disold,MSG_U(I_DISOLD,"Disable old accounts")); static const char *tbdelmode[]={ MSG_U(I_ARCHIVE,"Archive files"), MSG_U(I_DELFILE,"Delete files"), MSG_U(I_DONOTHING,"Keep files"), NULL }; dia.newf_chkm (MSG_U(F_DELMODE,"Deletion mode"),delmode,tbdelmode); dia.newf_title (MSG_U(I_UPDATES,"Updates"),1,"",MSG_R(I_UPDATES)); dia.auto_newline(true); dia.newf_chk (MSG_U(F_UPDATE,"Update"),updates.gecos,MSG_R(I_GECOS)); dia.newf_chk ("",updates.home,MSG_R(I_HOME)); dia.newf_chk ("",updates.group,MSG_R(I_GROUP)); dia.newf_chk ("",updates.altgr,MSG_R(I_ALTGR)); dia.newf_chk ("",updates.shell,MSG_R(I_SHELL)); dia.newf_chk ("",updates.passwd,MSG_R(I_PASSWD)); for (int i=0; iget(),updates.extra[i],k->getobjval()); } dia.setbutinfo (MENU_USR1,MSG_U(B_SAVECONFIG,"Save configuration") ,MSG_R(B_SAVECONFIG)); dia.setbutinfo (MENU_USR2,MSG_U(B_RUN,"Run"),MSG_R(B_RUN)); dia.setbutinfo (MENU_USR3,MSG_U(B_TEST,"Test"),MSG_R(B_TEST)); dia.setbutinfo (MENU_USR4,MSG_U(B_PREVIEW,"Preview"),MSG_R(B_PREVIEW)); dia.setbutinfo (MENU_USR5,MSG_U(B_DELCONFIG,"Del. config") ,MSG_R(B_DELCONFIG)); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_BATCHUSERS,"Account management") ,MSG_U(I_BATCHUSERS ,"You can update the Linux user account database\n" "from another database. This database is either\n" "a flat file, or retrieved using a command.") ,help_batch ,nof ,MENUBUT_CANCEL |MENUBUT_USR1|MENUBUT_USR2|MENUBUT_USR3|MENUBUT_USR4|MENUBUT_USR5); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage(config_msg)){ SSTRINGS tb; int nb = linuxconf_getall (K_ACCOUNTBATCH,K_INDEX,tb,false); if (nb == 0){ xconf_error (MSG_U(E_NOCONFIGYET,"No recorded configuration")); }else{ tb.sort(); DIALOG_MENUPOPUP pop; for (int i=0; iget()); } int no = 0; if (pop.editmenu ("",no)==MENU_OK){ config.setfrom (tb.getitem(no)->get()); accountbatch_loadconf (config.get(),conf,updates); dia.reload(); } } } }else if (code == MENU_USR1 || (code == MENU_ACCEPT && registry_mode)){ dia.save(); if (config.is_empty()){ xconf_error (MSG_U(E_NOCONFIG,"Enter the configuration name")); nof = 0; }else if (config.strchr(' ')!=NULL){ xconf_error (MSG_U(E_NOSPACE,"No space allowed in configuration name")); nof = 0; }else{ accountbatch_saveconfig (config.get(),conf,updates); } if (registry_mode) break; }else if (code == MENU_USR2 || code == MENU_USR3 || code == MENU_USR4){ dia.save(); if (conf.datafile.is_empty() && conf.datacmd.is_empty()){ xconf_error (MSG_U(E_FILECOMMAND ,"You must provide either a data file\n" "or a command")); nof = 1; }else if (!conf.datafile.is_empty() && !conf.datacmd.is_empty()){ xconf_error (MSG_U(E_FILEORCOMMAND ,"You must provide either a data file\n" "or a command, not both")); nof = 1; }else if (code == MENU_USR4){ SSTRING tmp(conf.datacmd); if (conf.datafile.is_filled()){ tmp.setfromf ("cat %s",conf.datafile.get()); } accountbatch_preview (tmp.get(),conf.parse); }else{ int nbgrp; if (conf.groups.cmp("*")==0){ nbgrp = 1; }else{ set tbgrp; nbgrp = account_cnvgroups (conf.groups.get(),groups,tbgrp); } if (nbgrp == -1){ nof = field_groups + 1; }else if (tasks.delold && nbgrp == 0 && conf.mingroup == 0){ xconf_error (MSG_U(E_NOGROUP ,"All account not part of the list (data file or command)\n" "will be deleted. You must restrict the scope of this action\n" "by supplying either a group list or a minimum group ID\n" "on which the list is authoritative")); }else if (tasks.delold && nbgrp > 0 && conf.mingroup > 0){ xconf_error (MSG_U(E_MINGROUPOR ,"Specifying a minimum group ID\n" "and a group list is ambiguous")); nof = field_groups; }else if (tasks.delold && tasks.disold){ xconf_error (MSG_U(E_DELORDIS ,"You select either\n" " \"delete old accounts\"\n" "or \"disable old accounts\"\n" "not both.")); }else if (!tasks.addmissing && !tasks.delold && !tasks.disold && !tasks.updatefields){ xconf_error (MSG_U(E_NOTASK ,"No task selected, nothing to do\n" "You must select at least one task:\n" "\tAdd missing accounts\n" "\tUpdate existing account password\n" "\tDelete or disable old accounts")); }else{ SSTRING tmp(conf.datacmd); if (conf.datafile.is_filled()){ tmp.setfromf ("cat %s",conf.datafile.get()); } static USER_DELOPER tbdeloper[]={ DELOPER_ARCHIVE, DELOPER_DELETE, DELOPER_KEEP }; tasks.testing = code == MENU_USR3; tasks.deloper = tbdeloper[delmode]; int ret = accountbatch_process (tasks,updates,conf ,tmp.get()); if (ret >= 0 && code == MENU_USR2) break; } } }else if (code == MENU_USR5){ dia.save(); if (config.is_empty()){ xconf_error (MSG_R(E_NOCONFIG)); nof = 0; }else if (config.strchr(' ')!=NULL){ xconf_error (MSG_R(E_NOSPACE)); nof = 0; }else{ accountbatch_delconfig (config.get()); } } } } static int accountbatch_command (int argc, char *argv[]) { int ret = -1; bool error = false; const char *passdb = "/var/run/password-generated"; ACCT_CONFIG conf; ACCT_TASKS tasks; ACCT_UPDATES updates; tasks.batchmode = true; conf.mingroup = -1; error_setmode (true); for (int i=0; i0){ acct_savepasswd (stats.passdb,passdb); } } } return ret; } ("accountbatch",PACKAGE_REV); tb.add (new SSTRING(MSG_U(T_USAGE ,"linuxconf --modulemain accountbatch usage\n" "\n" " --help\n" " --update [--config config-name] [--test] [--silent] [--file path]\n" " [--group groupname ] [--grouplow gid ]\n" " [--add ] [--del ] [--dis] [--mod ]\n" " [--archive ] [--delfiles ]\n" " [--passdb log_file ]\n" ))); if (context == MENU_USER_POLICIES){ keymenu = MSG_U(M_accountbatch,"Create/Update from a database"); dia.new_menuitem ("accountbatch","",keymenu); } if (context == MENU_USER_POLICIES){ if (key == keymenu){ accountbatch_dialog(NULL,false); } } return 0; int ret = -1; if (argc == 1){ accountbatch_dialog(NULL,false); }else if (argc > 2 && strcmp(argv[1],"--update")==0){ if (perm_rootaccess(MSG_R(P_MNGACCOUNT))){ ret = accountbatch_command (argc-2,argv+2); } }else{ printusage(); } return ret; static void accountbatch_edit (const char *config, bool setting) { accountbatch_dialog(config,true); } static void accountbatch_list (SSTRINGS &tb) { linuxconf_getall (K_ACCOUNTBATCH,K_INDEX,tb,true); } #include static PUBLISH_VARIABLES_MSG acct_vars[]={ {"config",P_MSG_R(F_CONFIGNAME)}, {"datafile",P_MSG_R(F_DATAFILE)}, {"datacmd",P_MSG_R(F_DATACMD)}, {"mingroup",P_MSG_R(F_MINGROUP)}, {"ongroups",P_MSG_R(F_ONGROUPS)}, {NULL, NULL} }; static REGISTER_VARIABLES vars ("accountbatch","configs",acct_vars,NULL ,accountbatch_edit,accountbatch_list);