#include #include #include #include #include #include "internal.h" #include "userconf.h" #include "userconf.m" #include #include #include #include static USERCONF_HELP_FILE help_cron ("cron"); class CRON_ENTRY: public ARRAY_OBJ{ public: SSTRING comment; SSTRING minutes; SSTRING hours; SSTRING dmonths; // Days of the month SSTRING dweeks; // Days of the week SSTRING months; SSTRING command; char enable; // The entry is active /*~PROTOBEG~ CRON_ENTRY */ public: CRON_ENTRY (const char *buf); CRON_ENTRY (void); int edit (const char *suggest[][2]); int is_comment (void); private: void parse (const char *buf); public: void write (FILE *fout); /*~PROTOEND~ CRON_ENTRY */ }; PRIVATE void CRON_ENTRY::parse(const char *buf) { buf = minutes.copyword (buf); buf = hours.copyword (buf); buf = dmonths.copyword (buf); buf = months.copyword (buf); buf = dweeks.copyword (buf); buf = str_skip(buf); command.setfrom (buf); } PUBLIC CRON_ENTRY::CRON_ENTRY(const char *buf) { /* #Specification: crontab / enabling en entry The crontab files do not have a reliable way to enable/disable an entry. While we can comment out one line, there is no standard way to do it and let linuxconf differentiate a disabled entry from a simple comment. Linuxconf is using a special "token" which should make it obvious that this is not an ordinary comment. Disabled entry are prefixed by the sequence #-#. This trick is now used for crontab and should make their way into other configuration file. */ const char *start = str_skip(buf); enable = 1; if (strncmp(start,"#-#",3)==0){ enable = 0; parse (start+3); }else if (start[0] == '#'){ comment.setfrom (buf); }else{ // A crontab file may contain variable definition like // MAILTO=email address. Those are managed as comments for now // (the user interface does not allow the user to edit them) while (*start > ' ' && *start != '=') start++; if (*start == '='){ comment.setfrom (buf); }else{ parse (buf); } } } PUBLIC CRON_ENTRY::CRON_ENTRY() { enable = 1; static const char def[]="*"; minutes.setfrom (def); hours.setfrom (def); dmonths.setfrom (def); months.setfrom (def); dweeks.setfrom (def); } static const char *str_or_star(const SSTRING &s) { const char *ret = s.get(); //if (*ret == '\0') ret = "*"; return ret; } /* Return != 0 if this entry is only a comment */ PUBLIC int CRON_ENTRY::is_comment() { return command.is_empty(); } PUBLIC void CRON_ENTRY::write (FILE *fout) { if (is_comment()){ fprintf (fout,"%s\n",comment.get()); }else{ fprintf (fout,"%s%s %s %s %s %s %s\n" ,enable ? "" : "#-# " ,str_or_star(minutes) ,str_or_star(hours) ,str_or_star(dmonths) ,str_or_star(months) ,str_or_star(dweeks) ,command.get()); } } static void cron_showopt ( FIELD_COMBO *comb, const char *unit, int mini, int maxi) { char buf[20]; sprintf (buf,"%d,%d,%d",mini,mini+1,mini+2); comb->addopt (buf,MSG_U(F_3VALUES,"Any of those three")); sprintf (buf,"%d-%d",mini,maxi); char buf2[100]; sprintf (buf2,MSG_U(F_CRONRANGE,"From %d to %d"),mini,maxi); comb->addopt (buf,buf2); sprintf (buf,"%d-%d/2",mini,maxi); sprintf (buf2,MSG_U(F_CRONSTEP,"From %d to %d step 2"),mini,maxi); comb->addopt (buf,buf2); sprintf (buf2,"%s %s",MSG_U(F_EVERY,"Every"),unit); comb->addopt ("*",buf2); sprintf (buf2,"%s %s",MSG_U(F_EVERY3,"Every 3"),unit); comb->addopt ("*/3",buf2); } static void cron_showopt ( FIELD_COMBO *comb, const char *unit, int mini, const char *mini_str, int maxi, const char *maxi_str) { char buf[20]; sprintf (buf,"%d",mini); comb->addopt (buf,mini_str); sprintf (buf,"%d",maxi); comb->addopt (buf,maxi_str); cron_showopt (comb,unit,mini,maxi); } static void cron_validate (SSTRING &field, int &errfield, int nofield) { if (errfield == -1){ const char *s = field.get(); if (strchr(s,' ')!=NULL){ xconf_error (MSG_U(E_NOSPACE,"No spaces allowed here")); errfield = nofield; }else if (strcmp(s,"*")!=0){ SSTRINGS tb; int nb = str_splitline (s,',',tb); for (int i=0; iget(); const char *startelm = elm; bool dash_seen = false,slash_seen=false,err=false; while (*elm != '\0'){ if (*elm == '-'){ if (dash_seen) err = true; if (slash_seen) err = true; dash_seen = true; }else if (*elm == '/'){ if (startelm[0] == '*'){ if (elm != startelm+1) err = true; }else if (!dash_seen){ err = true; } slash_seen = true; } elm++; } if (err){ xconf_error (MSG_U(E_IVLDPERIOD,"Invalid period definition")); errfield = nofield; } } } } } /* Edit one entry Return -1 if abort, 0 if accept, 1 if delete */ PUBLIC int CRON_ENTRY::edit (const char *suggest[][2]) { DIALOG dia; dia.newf_chk ("",enable,MSG_U(F_ENABLED,"This entry is active")); FIELD_COMBO *comb; if (suggest != NULL){ comb = dia.newf_combo (MSG_U(F_COMMAND,"Command"),command); for (int i=0; suggest[i][0] != NULL; i++){ comb->addopt (suggest[i][0],suggest[i][1]); } }else{ dia.newf_str (MSG_R(F_COMMAND),command); } int start_field = dia.getnb(); comb = dia.newf_combo (MSG_U(F_MONTHS,"Months"),months); cron_showopt (comb,MSG_U(F_MONTH,"month"),1 ,MSG_U(F_ISJANUARY,"is January") ,12,MSG_U(F_ISDECEMBER,"is December")); comb = dia.newf_combo (MSG_U(F_DMONTH,"Days of the month"),dmonths); cron_showopt (comb,MSG_U(F_DAYOFMONTH,"days of the month"),1,31); comb = dia.newf_combo (MSG_U(F_DWEEK,"Days of the week"),dweeks); cron_showopt (comb,MSG_U(F_DAYOFWEEK,"days of the week") ,0,MSG_U(F_ISSUNDAY,"is Sunday") ,6,MSG_U(F_ISSATURDAY,"is Saturday")); comb = dia.newf_combo (MSG_U(F_HOURS,"Hours"),hours); cron_showopt (comb,MSG_U(F_hOURS,"hours"),0,23); comb = dia.newf_combo (MSG_U(F_MINUTES,"Minutes"),minutes); cron_showopt (comb,MSG_U(F_mINUTES,"minutes"),0,59); for (int i=1; iset_noempty(); int nof = 0; int ret = -1; while (1){ MENU_STATUS code = dia.edit ( MSG_U(T_CRONENTRY,"Schedule job definition") ,MSG_U(I_CRONENTRY ,"You can define a command which will be\n" "followed a precise schedule") ,help_cron ,nof,MENUBUT_DEL|MENUBUT_ACCEPT|MENUBUT_CANCEL); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_DEL){ if (xconf_delok()){ ret = 1; break; } }else{ int errfield = -1; cron_validate (months,errfield,start_field); cron_validate (dmonths,errfield,start_field+1); cron_validate (dweeks,errfield,start_field+2); cron_validate (hours,errfield,start_field+3); cron_validate (minutes,errfield,start_field+4); if (errfield == -1){ ret = 0; break; }else{ nof = errfield; } } } if (ret != 0) dia.restore(); return ret; } class CRONTAB: public ARRAY{ SSTRING user; /*~PROTOBEG~ CRONTAB */ public: CRONTAB (const char *_user); int addcmd (const char *cmd, const char *minutes, const char *hours, const char *dmonths, const char *dweeks, const char *months); int delcmd (const char *cmd); int edit (const char *suggest[][2]); CRON_ENTRY *getitem (int no); int write (void); /*~PROTOEND~ CRONTAB */ }; static int cron_formatcmd(const char *user, const char *options, char *cmd) { int ret = -1; DAEMON_INTERNAL *dae = daemon_find ("crontab"); if (dae != NULL && dae->is_managed()){ sprintf (cmd,"%s %s -u %s",dae->getpath(),options,user); ret = 0; } return ret; } /* Execute the crontab command for a specific user */ int cron_install (const char *user, const char *options) { int ret = -1; char cmd[200]; if (cron_formatcmd(user,options,cmd)!=-1){ POPEN po (cmd); if (po.isok()){ po.wait(20); ret = po.getstatus() == 0 ? 0 : -1; } } return ret; } PUBLIC CRONTAB::CRONTAB (const char *_user) { user.setfrom (_user); char cmd[200]; if (cron_formatcmd(_user,"-l",cmd)!=-1){ POPEN pop (cmd); while (pop.wait(10)>0){ char line[1000]; while (pop.readout(line,sizeof(line)-1) != -1){ strip_end (line); if (line[0] != '\0') add (new CRON_ENTRY(line)); } } } } PUBLIC CRON_ENTRY *CRONTAB::getitem (int no) { return (CRON_ENTRY*)ARRAY::getitem(no); } PUBLIC int CRONTAB::write () { int ret = -1; if (perm_rootaccess(MSG_U(P_CRON,"maintain cron files"))){ int n = getnb(); char cmd[200]; if (n==0){ cron_install (user.get(),"-r 2>/dev/null"); }else if (cron_formatcmd(user.get(),"-",cmd)!=-1){ POPEN po (cmd); ret = -1; if (po.isok()){ FILE *fout = po.getfout(); for (int i=0; iwrite(fout); ret = po.close() == 0 ? 0 : -1; } } } return ret; } PUBLIC int CRONTAB::edit(const char *suggest[][2]) { DIALOG_RECORDS dia; dia.setkeyformat (HTML_KEY_INDEX); dia.newf_head ("",MSG_U(H_CRONJOBS,"Status\tCommand")); int nof=0; while (1){ int lookup[getnb()]; int n = 0; for (int i=0; iis_comment()){ dia.set_menuitem(n,e->enable ? "[X]" : "[ ]" ,e->command.get()); lookup[n++] = i; } } dia.remove_last (n+1); char title[100]; sprintf (title,MSG_U(T_JOBS,"Schedule jobs for account %s") ,user.get()); MENU_STATUS code = dia.editmenu (title ,MSG_U(I_JOBS,"Here is the list of scheduled tasks\n" "associated with an account.\n" "Some may be disabled.") ,help_cron ,nof,MENUBUT_ADD); if (code == MENU_ESCAPE || code == MENU_QUIT){ break; }else if (code == MENU_ADD){ CRON_ENTRY *e = new CRON_ENTRY; int ok = e->edit (suggest); manage_edit (e,ok); }else if (nof >=0 && nof < n){ CRON_ENTRY *e = getitem(lookup[nof]); int ok = e->edit (suggest); manage_edit (e,ok); } } return 0; } /* Edit the crontab of one user */ void cron_edit ( const char *user, const char *suggest[][2]) // Suggest command and explanation // terminate by NULL entry { if (perm_rootaccess(MSG_R(P_CRON))){ CRONTAB ctab(user); ctab.edit(suggest); } } void cron_edit (const char *user) { cron_edit (user,NULL); } /* Return != 0 if this user has no crontab */ int cron_isempty (const char *user) { CRONTAB ctab(user); return ctab.getnb() == 0; } PUBLIC int CRONTAB::addcmd( const char *cmd, const char *minutes, const char *hours, const char *dmonths, const char *dweeks, const char *months) { int ret = 0; bool found = false; for (int i=0; icommand.cmp(cmd)==0){ found = true; break; } } if (!found){ CRON_ENTRY *c = new CRON_ENTRY; c->command.setfrom (cmd); c->minutes.setfrom (minutes); c->hours.setfrom (hours); c->dmonths.setfrom (dmonths); c->dweeks.setfrom (dweeks); c->months.setfrom (months); add (c); ret = write(); } return ret; } PUBLIC int CRONTAB::delcmd( const char *cmd) { int ret = 0; for (int i=0; icommand.cmp(cmd)==0){ remove_del (c); ret = write (); break; } } return ret; } /* Add one command to the cron of a user If the command is already there, it is kept unchanged. This means that the provided schedule is only a hint used when the command is missing from this user cron. */ int cron_addcmd ( const char *user, const char *cmd, const char *minutes, const char *hour, const char *dmonths, const char *dweeks, const char *months) { CRONTAB cron (user); return cron.addcmd (cmd,minutes,hour,dmonths,dweeks,months); } int cron_delcmd ( const char *user, const char *cmd) { CRONTAB cron (user); return cron.delcmd (cmd); }