#include #include #include #include "mgettyconf.h" #include "mgettyconf.m" #include #include #include #include static HELP_FILE help_mgetty ("mgetty","mgetty"); static HELP_FILE help_ports ("mgetty","port"); static HELP_FILE help_login ("mgetty","login"); static HELP_FILE help_fax ("mgetty","fax"); static const char *subsys_mgetty = "mgetty"; static LINUXCONF_SUBSYS subsysm (subsys_mgetty,P_MSG_R(M_mgetty)); static CONFIG_FILE f_mgetty ("/etc/mgetty+sendfax/mgetty.config",help_ports ,CONFIGF_MANAGED ,"root","root",0644 ,subsys_mgetty); static CONFIG_FILE f_login ("/etc/mgetty+sendfax/login.config",help_login ,CONFIGF_MANAGED ,"root","root",0600 ,subsys_mgetty); static CONFIG_FILE f_inittab ("/etc/inittab",help_nil ,CONFIGF_MANAGED|CONFIGF_NOARCH ,"root","root",0644); class MGETTY_OPT: public ARRAY_OBJ{ public: VIEWITEM *it; SSTRING val; const char *keyw; char on; // Converted value of "val" used with DIALOG::newf_chk /*~PROTOBEG~ MGETTY_OPT */ public: MGETTY_OPT (VIEWITEM *_it, const char *_val); MGETTY_OPT (void); void bool2str (void); void loadif (VIEWITEM *_it, const char *word, const char *arg); void str2bool (void); void update (VIEWITEMS&views); /*~PROTOEND~ MGETTY_OPT */ }; PUBLIC MGETTY_OPT::MGETTY_OPT() { it = NULL; keyw = NULL; } PUBLIC MGETTY_OPT::MGETTY_OPT(VIEWITEM *_it, const char *_val) { it = _it; val.setfrom (_val); } PUBLIC void MGETTY_OPT::str2bool() { on = val.icmp("y")==0; } PUBLIC void MGETTY_OPT::bool2str() { val.setfrom (on ? "y" : ""); } PUBLIC void MGETTY_OPT::update ( VIEWITEMS &views) { if (val.is_empty()){ if (it != NULL){ views.remove_del (it); it = NULL; } }else{ if (it == NULL){ it = new VIEWITEM(""); views.add (it); } it->line.setfrom (keyw); it->line.append (" "); it->line.append (val.get()); } } class MGETTY_OPTS: public ARRAY{ public: VIEWITEMS views; MGETTY_OPT speed; MGETTY_OPT debug; MGETTY_OPT fax_id; MGETTY_OPT port_owner; MGETTY_OPT port_group; MGETTY_OPT port_mode; MGETTY_OPT fax_owner; MGETTY_OPT fax_group; MGETTY_OPT fax_mode; MGETTY_OPT data_only; MGETTY_OPT switchbd; MGETTY_OPT init_chat; MGETTY_OPT statistics_chat; MGETTY_OPT statistics_file; MGETTY_OPT modem_type; MGETTY_OPT direct; MGETTY_OPT toggle_dtr; /*~PROTOBEG~ MGETTY_OPTS */ public: MGETTY_OPTS (void); int edit (void); int editfax (void); MGETTY_OPT *getitem (int no)const; void parse (VIEWITEMS&gviews); void setupdia (DIALOG&dia); void update (void); /*~PROTOEND~ MGETTY_OPTS */ }; PUBLIC MGETTY_OPTS::MGETTY_OPTS() { speed.keyw = "speed"; debug.keyw = "debug"; fax_id.keyw = "fax-id"; port_owner.keyw = "port-owner"; port_group.keyw = "port-group"; port_mode.keyw = "port-mode"; fax_owner.keyw = "fax-owner"; fax_group.keyw = "fax-group"; fax_mode.keyw = "fax-mode"; data_only.keyw = "data-only"; switchbd.keyw = "switchbd"; init_chat.keyw = "init-chat"; statistics_chat.keyw = "statistics-chat"; statistics_file.keyw = "statistics-file"; modem_type.keyw = "modem-type"; direct.keyw = "direct"; toggle_dtr.keyw = "toggle-dtr"; } /* Add a baud rate field in a dialog with standard values */ static void baud_setfield ( SSTRING &baudstr, DIALOG &dia) { char msg[10]; FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_BAUD,"Baud rate"),baudstr); static short int tb[]={ 3,6,12,24,48,96, 192,384,576,1152 }; for (unsigned i=0; iaddopt (msg); } } PUBLIC void MGETTY_OPTS::setupdia (DIALOG &dia) { dia.newf_title (MSG_U(T_BASIC,"Basic"),1,"",MSG_R(T_BASIC)); baud_setfield (speed.val,dia); dia.newf_str (MSG_U(F_INITCHAT,"Initialisation chat"),init_chat.val); dia.newf_str (MSG_U(F_MODEMTYPE,"Modem type"),modem_type.val); dia.newf_title (MSG_U(T_FILEMODE,"File setup"),1,"",MSG_R(T_FILEMODE)); dia.newf_str (MSG_U(F_PORTOWNER,"Port owner"),port_owner.val); dia.newf_str (MSG_U(F_PORTGROUP,"Port group"),port_group.val); dia.newf_str (MSG_U(F_PORTMODE,"Port mode"),port_mode.val); dia.newf_title (MSG_U(T_FEATURE,"Feature"),1,"",MSG_R(T_FEATURE)); data_only.str2bool(); dia.newf_chk ("",data_only.on,MSG_U(F_DATAONLY,"Data only")); direct.str2bool(); dia.newf_chk ("",direct.on,MSG_U(F_DIRECT,"Direct link (no modem)")); toggle_dtr.str2bool(); dia.newf_chk ("",toggle_dtr.on,MSG_U(F_TOGGLEDTR,"Toggle dtr")); dia.newf_str (MSG_U(F_SWITCHBD,"Switch baud rate (fax)"),switchbd.val); dia.newf_str (MSG_U(F_DEBUG,"Debug level"),debug.val); dia.newf_str (MSG_U(F_STATCHAT,"Statistics chat"),statistics_chat.val); dia.newf_str (MSG_U(F_STATFILE,"Statistics file"),statistics_file.val); } PUBLIC void MGETTY_OPTS::update() { speed.update (views); debug.update (views); port_owner.update (views); port_group.update (views); port_mode.update (views); data_only.bool2str(); data_only.update (views); switchbd.update (views); init_chat.update (views); statistics_chat.update (views); statistics_file.update (views); modem_type.update (views); direct.bool2str(); direct.update (views); toggle_dtr.bool2str(); toggle_dtr.update (views); } /* Edit default configuration for ttys */ PUBLIC int MGETTY_OPTS::edit() { int ret = 0; DIALOG dia; setupdia (dia); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_DEFAULT,"Defaults") ,"" ,help_mgetty ,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ update(); ret = 1; break; } } return ret; } /* Edit base configuration for incoming faxes */ PUBLIC int MGETTY_OPTS::editfax() { int ret = 0; DIALOG dia; dia.newf_str (MSG_U(F_FAXID,"Fax id"),fax_id.val); dia.newf_str (MSG_U(F_FAXOWNER,"Fax owner"),fax_owner.val); dia.newf_str (MSG_U(F_FAXGROUP,"Fax group"),fax_group.val); dia.newf_str (MSG_U(F_FAXMODE,"Fax mode"),fax_mode.val); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_FAXSET,"Fax settings") ,"" ,help_fax ,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ fax_id.update (views); fax_owner.update (views); fax_group.update (views); fax_mode.update (views); ret = 1; break; } } return ret; } PUBLIC MGETTY_OPT *MGETTY_OPTS::getitem (int no) const { return (MGETTY_OPT*)ARRAY::getitem(no); } PUBLIC void MGETTY_OPT::loadif ( VIEWITEM *_it, const char *word, const char *arg) { if (strcmp(word,keyw)==0){ it = _it; val.setfrom (arg); } } PUBLIC void MGETTY_OPTS::parse (VIEWITEMS &gviews) { while (gviews.getnb()>0){ VIEWITEM *it = gviews.getitem(0); char word[200]; const char *arg = str_copyword (word,it->line.get(),sizeof(word)); arg = str_skip (arg); if (strcmp(word,"port")==0){ break; }else{ gviews.remove (it); views.add (it); speed.loadif (it,word,arg); debug.loadif (it,word,arg); fax_id.loadif (it,word,arg); port_owner.loadif (it,word,arg); port_group.loadif (it,word,arg); port_mode.loadif (it,word,arg); fax_owner.loadif (it,word,arg); fax_group.loadif (it,word,arg); fax_mode.loadif (it,word,arg); data_only.loadif (it,word,arg); switchbd.loadif (it,word,arg); init_chat.loadif (it,word,arg); statistics_chat.loadif (it,word,arg); statistics_file.loadif (it,word,arg); modem_type.loadif (it,word,arg); direct.loadif (it,word,arg); toggle_dtr.loadif (it,word,arg); } } } class INITTAB_REC{ public: char id[5]; char runlevels[7]; char verb[20]; // respawn or other inittab verb char command[PATH_MAX]; // Path of the command char arg1[PATH_MAX]; // First argument char end[PATH_MAX]; // other args /*~PROTOBEG~ INITTAB_REC */ public: INITTAB_REC (void); int parse (const char *line); bool reformat (SSTRING&line); /*~PROTOEND~ INITTAB_REC */ }; PUBLIC INITTAB_REC::INITTAB_REC() { id[0] = verb[0] = command[0] = arg1[0] = end[0] = '\0'; memset (runlevels,0,sizeof(runlevels)); } /* Parse a line of the inittab file return -1 if the line is not valid. */ PUBLIC int INITTAB_REC::parse (const char *line) { int ret = -1; char tmp[strlen(line)+1]; strcpy (tmp,line); char *pt = strchr(tmp,':'); if (pt != NULL){ *pt++ = ':'; strcpy_cut (id,tmp,sizeof(id)); char *start = pt; pt = strchr(pt,':'); if (pt != NULL){ *pt++ = '\0'; while (*start != '\0'){ char car = *start++; if (isdigit(car)){ car -= '0'; runlevels[car] = 1; } } start = pt; pt = strchr(pt,':'); if (pt != NULL){ *pt++ = '\0'; strcpy_cut (verb,start,sizeof(verb)); pt = str_copyword (command,pt,sizeof(command)); pt = str_copyword (arg1,pt,sizeof(arg1)); pt = str_skip(pt); strcpy_cut (end,pt,sizeof(end)); ret = 0; } } } return ret; } /* Format back a parse /etc/inittab record Return true if the line was changed */ PUBLIC bool INITTAB_REC::reformat (SSTRING &line) { char tmp[3*PATH_MAX]; char levels[7]; char *pt = levels; for (int i=1; i<7; i++){ if (runlevels[i]) *pt++ = '0' + i; } *pt = '\0'; snprintf (tmp,sizeof(tmp)-1,"%s:%s:%s:%s %s %s" ,id,levels,verb,command,arg1,end); bool ret = line.cmp(tmp)!=0; line.setfrom (tmp); return ret; } class MGETTY_PORT: public ARRAY_OBJ{ public: VIEWITEMS views; MGETTY_OPT port; MGETTY_OPTS opts; VIEWITEM *inittab_it; INITTAB_REC rec; char enabled; // Is the port enabled in /etc/inittab /*~PROTOBEG~ MGETTY_PORT */ public: MGETTY_PORT (VIEWITEM *it, const char *name); MGETTY_PORT (void); int edit (void); bool updateinittab (VIEWITEMS&inittab); /*~PROTOEND~ MGETTY_PORT */ }; PUBLIC MGETTY_PORT::MGETTY_PORT (VIEWITEM *it, const char *name) { port.it = it; port.val.setfrom (name); port.keyw = "port"; opts.views.add (it); inittab_it = NULL; enabled = 0; } PUBLIC MGETTY_PORT::MGETTY_PORT () { port.keyw = "port"; inittab_it = NULL; enabled = 1; } /* Return the path of the mgetty utility */ static const char *mgetty_getpath() { const char *ret = "/sbin/mgetty"; DAEMON_INTERNAL *dae = daemon_find ("mgetty"); if (dae != NULL){ ret = dae->getpath(); } return ret; } /* Update the mgetty entry in /etc/inittab for this port Return true if inittab was modified. */ PUBLIC bool MGETTY_PORT::updateinittab(VIEWITEMS &inittab) { /* #Specification: updating /etc/inittab / strategy the mgetty linuxconf module update /etc/inittab and somewhat synchronise it with mgetty.config. Mostly, if a port is enabled its entry is updated/added in /etc/inittab. If the port is disabled and there is no entry in /etc/inittab, none is added. If the port is disabled, and there is an entry, the entry is turned to off. */ bool ret = false; if (enabled){ if (inittab_it == NULL){ // New entry inittab_it = new VIEWITEM(""); inittab.add (inittab_it); strcpy (rec.command,mgetty_getpath()); } strcpy (rec.verb,"respawn"); }else if (inittab_it != NULL){ // It must be turned off strcpy (rec.verb,"off"); } if (inittab_it != NULL){ port.val.copy (rec.arg1); // We have either /dev/ttyXX or simply ttyXX. We skip de path const char *pt = strrchr(rec.arg1,'/'); if (pt == NULL){ pt = rec.arg1; }else{ pt++; } if (strlen(pt)>3) pt+=3; // Keep only the last character // as the ID. Init used that to // manage the utmp file strcpy (rec.id,pt); ret = rec.reformat (inittab_it->line); } return ret; } /* Edit a port configuration Return 0 if the port was modified, 1 if the port configuration was deleted and -1 if nothing was changed (cancel) */ PUBLIC int MGETTY_PORT::edit () { int ret = -1; DIALOG dia; dia.newf_str (MSG_U(F_PORT,"Serial port"),port.val); dia.last_noempty(); dia.newf_chk ("",enabled,MSG_U(F_ENABLED,"is enabled")); opts.setupdia (dia); dia.newf_title (MSG_U(T_RUNLEVELS,"Runlevels"),1,"",MSG_R(T_RUNLEVELS)); dia.newf_chk (MSG_U(F_ENPORT,"Enable port for"),rec.runlevels[2] ,MSG_U(F_RUNLEVEL2,"Init runlevel 2")); dia.newf_chk ("",rec.runlevels[3],MSG_U(F_RUNLEVEL3,"Init runlevel 3")); dia.newf_chk ("",rec.runlevels[4],MSG_U(F_RUNLEVEL4,"Init runlevel 4")); dia.newf_chk ("",rec.runlevels[5],MSG_U(F_RUNLEVEL5,"Init runlevel 5")); dia.delwhat (MSG_U(I_DELREC,"the record")); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_PORT,"One serial port") ,"" ,help_mgetty ,nof,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_DEL); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_DEL){ if (xconf_delok()){ ret = 1; break; } }else{ int i; for (i=1; i<7; i++){ if (rec.runlevels[i]) break; } if (i == 7 && enabled){ xconf_notice (MSG_U(N_NORUNLEVEL,"No runlevel selected\n" "even if the port is enabled")); } port.update (opts.views); opts.update (); ret = 0; break; } } return ret; } class MGETTY_PORTS: public ARRAY { /*~PROTOBEG~ MGETTY_PORTS */ public: MGETTY_PORT *getitem (int no)const; void parse (VIEWITEMS&views); void setport (INITTAB_REC&r, VIEWITEM *it); /*~PROTOEND~ MGETTY_PORTS */ }; PUBLIC MGETTY_PORT *MGETTY_PORTS::getitem (int no) const { return (MGETTY_PORT*)ARRAY::getitem(no); } PUBLIC void MGETTY_PORTS::parse (VIEWITEMS &views) { while (views.getnb()>0){ VIEWITEM *it = views.getitem(0); char word[200]; const char *arg = str_copyword (word,it->line.get(),sizeof(word)); arg = str_skip (arg); assert (strcmp(word,"port")==0); views.remove(it); MGETTY_PORT *port = new MGETTY_PORT (it,arg); port->opts.parse (views); add (port); } } class MGETTY_CONF{ VIEWITEMS views; MGETTY_OPTS def; MGETTY_PORTS ports; VIEWITEMS inittab; /*~PROTOBEG~ MGETTY_CONF */ public: MGETTY_CONF (void); void editdefault (void); void editfax (void); void editports (void); int write (bool writeinittab); /*~PROTOEND~ MGETTY_CONF */ }; /* Add the inittab information to on port definition. If the port definition is missing, it is added */ PUBLIC void MGETTY_PORTS::setport( INITTAB_REC &r, VIEWITEM *it) { MGETTY_PORT *port = NULL; for (int i=0; iport.val.cmp(r.arg1)==0){ port = p; break; } } if (port == NULL){ port = new MGETTY_PORT; add (port); } port->port.val.setfrom (r.arg1); port->inittab_it = it; port->rec = r; port->enabled = strcmp(r.verb,"respawn")==0; } PUBLIC MGETTY_CONF::MGETTY_CONF() { views.read (f_mgetty); def.parse (views); ports.parse(views); /* #Specification: ports / mgetty.config and /etc/inittab A port handled by mgetty has to be hooked in /etc/inittab. It may also have some setting in mgetty.config, but this is not essential if this port share the default setting. We read mgetty.config to get the port description and other settings and then we read /etc/inittab */ inittab.read (f_inittab); const char *path = mgetty_getpath(); if (path != NULL){ for (int i=0; iline.get()) != -1 && strcmp(r.command,path)==0 && r.arg1[0] != '\0' && r.end[0] == '\0'){ // Ok, this is a mgetty line with a single argument // (a port then) ports.setport (r,v); } } } } static void mgetty_stuff (const VIEWITEMS &from, VIEWITEMS &to) { for (int i=0; iopts.views.getnb() > 1){ mgetty_stuff (p->opts.views,views); } writeinittab |= p->updateinittab (inittab); } int ret = views.write (f_mgetty,NULL); if (ret != -1 && writeinittab) inittab.write (f_inittab,NULL); views.remove_all(); return ret; } PUBLIC void MGETTY_CONF::editdefault() { if (def.edit() != 0) write(false); } PUBLIC void MGETTY_CONF::editfax() { if (def.editfax() != 0) write(false); } static int cmp_by_port (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { MGETTY_PORT *p1 = (MGETTY_PORT*)o1; MGETTY_PORT *p2 = (MGETTY_PORT*)o2; return p1->port.val.cmp(p2->port.val); } PUBLIC void MGETTY_CONF::editports() { DIALOG_LISTE *dia = NULL; int nof = 0; while (1){ if (dia == NULL){ dia = new DIALOG_LISTE; dia->newf_head ("",MSG_U(H_PORTS,"Port\tEnabled\tRunlevel 2\t 3 \t 4 \t 5 ")); ports.sort (cmp_by_port); for (int i=0; ienabled ? 'X' : ' ' ,p->rec.runlevels[2] ? 'X' : ' ' ,p->rec.runlevels[3] ? 'X' : ' ' ,p->rec.runlevels[4] ? 'X' : ' ' ,p->rec.runlevels[5] ? 'X' : ' '); dia->new_menuitem(p->port.val.get(),buf); } } MENU_STATUS code = dia->editmenu (MSG_U(T_PORTS,"Serial ports") ,MSG_U(I_PORTS ,"Here is the list of serial ports configured\n" "to either answer the phone or react to direct\n" "serial connection.") ,help_ports ,nof,MENUBUT_ADD); bool must_delete = false; if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_ADD){ MGETTY_PORT *p = new MGETTY_PORT; int ok = p->edit(); if (ok == 0){ ports.add (p); write(false); must_delete = true; }else{ delete p; } }else{ MGETTY_PORT *p = ports.getitem(nof); if (p != NULL){ int ok = p->edit(); if (ok >= 0){ bool writeinittab = false; if (ok == 1){ if (p->inittab_it != NULL){ inittab.remove_del (p->inittab_it); writeinittab = true; } ports.remove_del (p); } write(writeinittab); must_delete = true; } } } if (must_delete){ delete dia; dia = NULL; } } delete dia; } static VIEWITEM *medit_loginsetf ( VIEWITEMS &views, const char *keyw, SSTRING &prefix, const char *def_prefix, SSTRING &cmd, const char *def_cmd) { VIEWITEM *it = views.locate (keyw); if (it == NULL){ it = new VIEWITEM (""); prefix.setfrom (keyw); prefix.append (" "); prefix.append (def_prefix); prefix.append (" "); cmd.setfrom (def_cmd); views.add (it); }else{ char buf[it->line.getlen()+1]; it->line.copy(buf); char *pt = buf; pt = str_skipword(pt); pt = str_skipword(pt); pt = str_skipword(pt); pt = str_skip (pt); cmd.setfrom (pt); *pt = '\0'; prefix.setfrom (buf); } return it; } static void medit_login() { VIEWITEMS views; views.read (f_login); DIALOG dia; SSTRING prefixpap, papcmd; VIEWITEM *it_pap = medit_loginsetf (views,"/AutoPPP/",prefixpap,"- a_ppp",papcmd ,"/usr/sbin/pppd auth"); SSTRING prefixlogin,login; VIEWITEM *it_login = medit_loginsetf (views,"*",prefixlogin,"- -",login ,"/bin/login @"); dia.newf_str (MSG_U(F_LOGINCMD,"Normal shell login"),login); FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_PAPCMD,"PPP PAP login command"),papcmd); comb->addopt ("/usr/lib/linuxconf/lib/paplogin"); comb->addopt ("/usr/sbin/pppd auth"); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_R(M_LOGIN) ,MSG_U(I_LOGIN ,"For each login type, you can set the startup command") ,help_login ,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ it_pap->line.setfrom (prefixpap); it_pap->line.append (papcmd.get()); it_login->line.setfrom (prefixlogin); it_login->line.append (login.get()); // Make sure the login default is at the end views.remove (it_login); views.add (it_login); if (views.write(f_login,NULL) != -1) break; } } } void mgetty_edit() { DIALOG_MENU dia; static const char *m_default=MSG_U(M_PORTDEFAULT,"Serial port defaults"); static const char *m_faxset=MSG_U(M_FAXSETTINGS,"Fax settings"); static const char *m_addport=MSG_U(M_ADDPORT,"Setup serial ports"); static const char *m_login=MSG_U(M_LOGIN,"Setup login utilities"); static const char *opts[]={ "", m_default, "", m_faxset, "", m_addport, "", m_login, NULL }; dia.new_menuitems (opts); int nof=0; while (1){ MENU_STATUS code = dia.editmenu (MSG_R(M_mgetty) ,MSG_U(I_mgetty ,"mgetty manage serial port for both\n" "PPP, shell and fax connection.\n" "You can configure it here.") ,help_mgetty ,nof,0); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ const char *s = dia.getmenustr(nof); if (s == m_default){ MGETTY_CONF conf; conf.editdefault(); }else if (s == m_faxset){ MGETTY_CONF conf; conf.editfax(); }else if (s == m_addport){ MGETTY_CONF conf; conf.editports(); }else if (s == m_login){ medit_login(); } } } }