#include #include #include #include "netconf.h" #include "internal.h" #include #include "netconf.m" #include #include "../paths.h" #include "../askrunlevel/askrunlevel.h" #include "hostinfo.h" static NETCONF_HELP_FILE help_thishost ("thishost"); static CONFIG_FILE f_HOSTNAME (ETC_HOSTNAME,help_nil ,CONFIGF_GENERATED|CONFIGF_OPTIONAL ,"root","root",0644 ,subsys_stationid); static MESSAGE_DEF updatedns ("updatedns"); /* Compose one entry of the menu. Simply add the current value to the menu string Return dst. */ char *menu_setupopt( char *dst, const char *menu, const char *curval) { if (curval[0] == '\0') curval = "Not set"; sprintf (dst,"%s (%s)",menu,curval); return dst; } void host_setmasklist(FIELD_COMBO *comb) { static const char *tbnet[][2]={ {"255.0.0.0", MSG_U(I_CLASS_A,"Class A network")}, {"255.255.0.0", MSG_U(I_CLASS_B,"Class B network")}, {"255.255.255.0", MSG_U(I_CLASS_C,"Class C network")}, {"255.255.255.128", MSG_U(I_SUBNET128,"25 bits sub-network")}, {"255.255.255.192", MSG_U(I_SUBNET192,"26 bits sub-network")}, {"255.255.255.224", MSG_U(I_SUBNET224,"27 bits sub-network")}, {"255.255.255.240", MSG_U(I_SUBNET240,"28 bits sub-network")}, {"255.255.255.248", MSG_U(I_SUBNET248,"29 bits sub-network")}, }; for (unsigned i=0; iaddopt (tbnet[i][0],tbnet[i][1]); } } struct D_INFO{ SSTRINGS tbmod; SSTRINGS tbdesc; char tbavail[300]; SSTRING tbirq[16]; }; static void netconf_setoneinter ( DIALOG &dia, INTER_INFO &itf, const char *title, const char *pad, D_INFO &di) { dia.newf_title (pad,1,"",title); dia.newf_chk ("",itf.enable,MSG_U(I_ADAPACTIVE,"Enabled")); { static const char *tbmode[]={ MSG_U(I_MANUAL,"Manual"), MSG_U(I_DHCP,"Dhcp"), MSG_U(I_BOOTP,"Bootp"), NULL }; dia.newf_chkm (MSG_U(F_CONFMODE,"Config mode"),itf.confmode,tbmode); } dia.newf_str (MSG_U(F_PRIMNAME,"Primary name + domain"),itf.name); dia.newf_str (MSG_U(F_ALIASES,"Aliases (opt)"),itf.others); dia.newf_str (MSG_U(F_IPADR,"IP address"),itf.ipaddr); FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_NETMASK,"Netmask (opt)"),itf.netmask); host_setmasklist (comb); #if 0 dia.newf_str (MSG_U(F_NETADR,"Network address (opt)"),itf.network); dia.newf_str (MSG_U(F_BCAST,"Broadcast (opt)"),itf.bcast); #endif comb = dia.newf_combo (MSG_U(F_NETDEV,"Net device") ,itf.device); devlist_setcombo (comb); comb = dia.newf_combo (MSG_U(F_MODULE,"Kernel module"),itf.module); for (int m=0; mget(); const char *desc = di.tbdesc.getitem(m)->get(); char buf[100]; snprintf (buf,sizeof(buf)-1,"%s %s",di.tbavail[m] ? MSG_U(F_MODINST,"(inst)") : " " ,desc); comb->addopt (mod,buf); } if (!distrib_isenhanced()){ dia.newf_chk ("",itf.pcmcia,MSG_U(I_PCMCIA,"PCMCIA device (removable)")); } dia.newf_str (MSG_U(F_MODIO,"I/O port (opt)"),itf.modio); comb = dia.newf_combo (MSG_U(F_MODIRQ,"Irq (opt)"),itf.modirq); for (int i=1; i<16; i++){ const char *desc = di.tbirq[i].get(); char irqstr[3]; sprintf (irqstr,"%d",i); comb->addopt (irqstr,desc); } } /* Validate IP number. An empty string is ok here. */ static bool host_validip( const char *aip, // IP number to validate const char *mask) // Optional netmask { bool ret = true; if (aip[0] != '\0') ret = ipnum_validip(aip,mask,true); return ret; } /* Validate a netmask. An empty string is ok here. */ static bool host_validmask( const char *mask) // netmask to validate { bool ret = true; if (mask[0] != '\0') ret = ipnum_validip(mask,false); return ret; } /* Validate the user input for one interface Return false if there is an error */ static bool netconf_itfok( INTER_INFO &itf, int nodev, int &nof) { bool ret = false; bool name_empty = itf.name.is_empty(); bool other_empty = itf.others.is_empty(); bool ip_empty = itf.ipaddr.is_empty(); bool ip_zero = strcmp(itf.ipaddr.get(),"0.0.0.0") == 0; bool netmask_empty = itf.netmask.is_empty(); bool manual = itf.confmode == INTER_MANUAL; if (!name_empty && itf.name.strchr(' ')!=NULL){ xconf_error (MSG_U(E_NOSPACEADAPT ,"Adaptor %d: No space allowed in primary name"), nodev); nof = 3; }else if ((ip_empty && manual) && !(name_empty && other_empty)){ xconf_error (MSG_U(E_NEEDIP, "Adaptor %d: Missing IP number"), nodev); nof = 5; }else if (ip_zero && !(name_empty && other_empty)) { xconf_error (MSG_U(E_IVLHOSTNAME, "Hostname invalid for IP number: %s"), itf.ipaddr.get()); nof = 3; }else if ((ip_zero && !netmask_empty) || !host_validmask(itf.netmask.get())){ xconf_error (MSG_U(E_IVLMASK,"Invalid netwask: %s"), itf.netmask.get()); nof = 6; }else if (!(ip_zero || host_validip(itf.ipaddr.get(),itf.netmask.get()))){ xconf_error (MSG_U(F_IVLDIP,"Invalid IP number: %s"), itf.ipaddr.get()); nof = 5; }else if (itf.device.is_empty() && (!ip_empty || !name_empty || !manual)){ xconf_error (MSG_U(E_NONETDEV,"Missing network device")); nof = 7; }else{ ret = true; } return ret; } #if 0 static bool netconf_checknonet(INTER_INFO &itf) { /* #Specification: netconf / basic host info / no IP for eth0 If the user do no put any IP address for the first adaptor (ETH0), it may be because it is a mistake, or because no network is available. In the later case, a good idea is to enter the IP number 127.0.0.1. This has the advantage that local networking will work (eg. export DISPLAY=host:0). We can't assume that this is what the user intent. One thing is sure, it is better to have a IP address there. So we are asking for a confirmation to the user. */ bool ret = true; if (itf.ipaddr.is_empty() && itf.confmode == INTER_MANUAL){ if (dialog_yesno (MSG_U(Q_NOIP,"Are you sure") ,MSG_U(I_NOIP ,"No IP address was entered for the first\n" "ethernet adaptor. Either it is a mistake\n" "(you forgot) or you don't have a network\n" "adaptor at all.\n" "\n" "If you don't have any network adaptor,\n" "it is a good idea to enter the IP number\n" "127.0.0.1, which correspond to a dummy\n" "internal network. This may help later with\n" "network aware application such as X11.\n" "\n" "Do you want me to enter 127.0.0.1 for you ?") ,help_thishost)==MENU_YES){ itf.ipaddr.setfrom ("127.0.0.1"); }else{ ret = false; } } return ret; } #endif static void host_sethostif (const char *oldname, const char *newname) { DIALOG dia; dia.settype (DIATYPE_POPUP); char change=0, reboot=0; dia.newf_chk (MSG_U(F_CHANGEITNOW,"Change it"),change,MSG_U(I_CHANGEITNOW,"now")); dia.newf_chk (MSG_U(F_REBOOTNOW,"Reboot"),reboot,MSG_U(I_REBOOTNOW,"now")); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_NEWHOSTHAME,"Host name changed") ,MSG_U(I_NEWHOSTNAME ,"You have changed the host name of the machine\n" "Several services uses the host name to setup defaults.\n" "(samba,apache, ...)\n" "The easiest way to get a healthy status after a host name\n" "change is to reboot.\n" "\n" "This dialog allows you to make the change effective immediatly,\n" "reboot, or do nothing") ,help_nil ,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ if (change){ thishost1_sethostname (newname); } if (reboot){ shutdown_control(); } break; } } } /* Edit the spec of the current host. Return -1 if any error or the user abort the process. */ static int netconf_edithost(HOSTINFO &info) { /* #Specification: netconf / this host setup The user can change fields for each adaptor. # primary name of this machine/adaptor aliase names of this machine/adaptor IP address Network Netmask # */ int ret = -1; int nofield = 0; DIALOG dia; int i; D_INFO di; modlist_get ("net",di.tbmod,di.tbdesc,di.tbavail); irqlist_get (di.tbirq); int nbedit = info.nbdev + 3; if (nbedit > NB_ETH) nbedit = NB_ETH; int tbfield[nbedit]; dia.newf_title (MSG_U(F_HOSTNAME,"Host name"),1,"",MSG_R(F_HOSTNAME)); dia.newf_str (MSG_U(F_FULLHOSTNAME,"Host name + domain"),info.hostname); dia.last_noempty(); for (i=0; isetname1 (name1); ptr->setipnum (ip.get()); }else if (ptr != NULL){ networks.remove (ptr); delete ptr; } } void netconf_saveinfo ( INTER_INFO &itf, HOSTS &hosts, NETWORKS &networks, const char *forcealias, const char *netname, const char *mskname, const char *bcastname) { const char *fullname = itf.name.get(); HOST *hst = hosts.getitem (forcealias); if (hst == NULL && fullname[0] != '\0') hst = hosts.getitem (fullname); HOST *net = networks.getitem (netname); HOST *msk = networks.getitem (mskname); HOST *bcast = networks.getitem (bcastname); if (!itf.ipaddr.is_empty() && (fullname[0] != '\0' || !itf.others.is_empty())){ if (itf.name.cmp(forcealias)!= 0){ host_forcealias (itf.others,forcealias); } if (hst == NULL){ hst = new HOST; hosts.add (hst); } hst->setname1 (fullname); hst->setipnum (itf.ipaddr.get()); hst->setothers(itf.others.get()); }else if (hst != NULL){ hosts.remove (hst); delete hst; } netconf_update_nettb (networks,mskname,itf.netmask,msk); #if 1 netconf_update_nettb (networks,netname,itf.network,net); netconf_update_nettb (networks,bcastname,itf.bcast,bcast); #endif } /* Add an option to a /etc/conf.module entry associated with a kernel module. Multiple option for a given module are added with commas. */ static void host_addopt ( const char *kmodule, // Kernel module SSTRING_KEYS &vals, const SSTRING &opt) { const char *val = opt.get(); if (val[0] != '\0'){ SSTRING_KEY *sk = vals.getobj(kmodule); if (sk == NULL){ vals.add (kmodule,val); }else{ sk->getobj()->appendf(",%s",val); } } } static void host_saveopt (const char *keyw, SSTRING_KEYS &vals) { for (int i=0; iget(),keyw,sk->getobjval()); } } /* Function used for qsort */ int host_cmp_inter (const void *p1, const void *p2) { INTER_INFO *i1 = *(INTER_INFO**)p1; INTER_INFO *i2 = *(INTER_INFO**)p2; return i1->device.cmp(i2->device); } int netconf_saveinfos ( HOSTS &hosts, NETWORKS &networks, HOSTINFO &info) { /* #Specification: network devices / storing information We save in distribution specific format, but also maintain the /etc/hosts /etc/networks strategy already used by linuxconf */ bool indist = hostinfo_saveindist(hosts,info); if (!indist) hostinfo_savehostname (info); INTER_INFO *tbi[NB_ETH]; for (int i=0; idevice.get(); const char *kmodule = pta->module.get(); if (*device != '\0' && *kmodule != '\0'){ modules_setalias (device,kmodule); host_addopt (kmodule,ios,pta->modio); host_addopt (kmodule,irqs,pta->modirq); } } host_saveopt ("io",ios); host_saveopt ("irq",irqs); return linuxconf_save(); } /* Edit the spec of the current host */ void netconf_edithost() { /* #Specification: hostname / where is it stored netconf use the plain /etc/hosts file to store more of the name information. There is no other file such as /etc/hostname or /etc/HOSTNAME. netconf assume that special entries in /etc/hosts will have known alias name. It will enforce the presence of those alias name. The primary name of the machine will be taken from the entry which the alias "loghost". This name and IP number will be used for the first ethernet adaptor (ETH0). The second ethernet adaptor will be located from the entry having the alias eth1_loghost. The third ethernet adaptor spec will be located from the entry having the alias eth2_loghost. */ HOSTS hosts; NETWORKS networks; HOSTINFO info; if (netconf_loadinfos(hosts,networks,info)!=-1){ SSTRING old_hostname(info.hostname); if (netconf_edithost (info) != -1){ { /* #Specification: /etc/hosts / hostname / force alias Linuxconf maintain the host name in /etc/hosts. It enters there the fully qualified domain and also simply the host name (without domain) as an alias. Few system utility won't work correctly if this is not set (syslogd for one). */ char name[200]; info.a[0].name.copy(name); char *pt = strchr(name,'.'); if (pt != NULL){ *pt = '\0'; host_forcealias (info.a[0].others,name); } } netconf_saveinfos(hosts,networks,info); hosts.setlocalhost(); hosts.write(); networks.write (); /* #Specification: netconf / basic host info / DNS Each time we update the basic host info (name and IP number of the adaptor), we synchronise the info in the DNS on this machine is one is available. This is done silently. Maybe we should let the user decide if he wants to update the DNS from that info. Why ? */ module_sendmessage (updatedns,0,NULL); /* #Specification: /etc/HOSTNAME /etc/HOSTNAME is maintained with the host name if the file exist. This is not an official config file, but exist on many distribution (all ?). Further, this is not the reference used by the distribution (often). So linuxconf keep it up to date, but does not read it. */ if (f_HOSTNAME.exist()){ FILE_CFG *fout = f_HOSTNAME.fopen ("w"); if (fout != NULL){ fprintf (fout,"%s\n",info.hostname.get()); fclose (fout); } } if(old_hostname.cmp(info.hostname)!=0){ host_sethostif(old_hostname.get() ,info.hostname.get()); } } } } #include static PUBLISH_VARIABLES_MSG host_var_list[]={ {"hostname",P_MSG_R(F_FULLHOSTNAME)}, {"hostadaptor1",P_MSG_R(F_PRIMNAME)}, {"ipadaptor1",P_MSG_R(F_IPADR)}, {"maskadaptor1",P_MSG_R(F_NETMASK)}, {"devadaptor1",P_MSG_R(F_NETDEV)}, {"adaptor1on",P_MSG_R(I_ADAPACTIVE)}, {"adaptor1mode",P_MSG_R(F_CONFMODE)}, {"adaptor1mod",P_MSG_R(F_MODULE)}, { NULL, NULL } }; static REGISTER_VARIABLES host_registry1("netbase",host_var_list ,NULL,netconf_edithost);