#include #include #include #include #include #include #include #include #undef clear #include #include #include #include #include "askrunlevel.h" #include "internal.h" #include #include #include #include "../main/main.h" #include #include "../paths.h" #include "../xconf/xconf.h" #include "askrunlevel.m" static HELP_FILE help_askrun (HELP_ASKRUN,"intro"); static HELP_FILE help_boot (HELP_ASKRUN,"boot"); static CONFIG_FILE f_profile ("/etc/PROFILE",help_nil ,CONFIGF_OPTIONAL|CONFIGF_GENERATED|CONFIGF_NOOLD|CONFIGF_NOARCH); /* #Specification: askrunlevel / intro When linux boot, it start /sbin/init which does some basic initialisation, then try to jump to its default run level. If this default run level is not defined and /sbin/askrunlevel do exist, then it is execute. The purpose of this program is to let the user select the run level in a meaningful way. Instead of numbers, it present a menu of configuration useful for a workstation. Also, askrunlevel try to validate which configuration are possible. There is no need to ask the user if he want to start X automatically (using xdm) unless X has been configured. To make it easy for the user, askrunlevel act also has a first time configuration utility. So if a configuration do not exist it let the user configure it right away. No need to boot twice (once to configure, and once to activate the configuration). If the user do not select anything in a specific time, the default run level is activate. The future will tell if this is true... */ static void askrunlevel_saynographic(const char *status_graphic) { xconf_error (MSG_U(E_GRAPH,"Can't do this\n" "because the Graphic mode is not yet\n" "configured\n\n%s"),status_graphic); } static void askrunlevel_saynographic_net( char *status_graphic, char *status_net) { xconf_error (MSG_U(E_GRAPHNET,"Can't do this\n" "because the Graphic mode\n" "and the Networking are not yet\n" "configured\n\n%s\n\n%s"),status_graphic,status_net); } static void askrunlevel_saynonet(char *status_net) { xconf_error (MSG_U(E_NET,"Can't do this\n" "because the Networking is not yet\n" "configured\n\n%s"),status_net); } /* Activate the timeout only if the system is freshly start. If uptime is old enough, it means askrunlevel was called after the user explicitly told init to do so, so the timeout feature is not useful here. Return the timeout value (in seconds) or 0. */ static int askrunlevel_enabletimeout(ASK_PARM &parm) { long uptime = sys_uptime(); int timeout = 0; // We assume that if /proc/uptime is not available, better // play safe and disable the timeout feature. if (uptime > 0){ /* #Specification: askrunlevel / automatic booting / timeout linuxconf will automaticly boot the system into the default runlevel (as configured) only if it is called at boot time (when called as /sbin/askrunlevel). There is two possibilities: Either the boot was normal (ie. fairly fast) or fairly slow (the system had to do a fsck after a crash). In the first case, it will boot as configured after the specified timeout. If the system took more than 120 seconds to boot it will still boot by itself, but will selected a timeout of one minute. The system will also activate the bell. This is to catch the attention of the operator. */ timeout = parm.timeout; if(uptime > 120 && timeout != 0){ timeout = 60; for (int i=0; i<2; i++){ putchar ('\a'); fflush (stdout); sleep(1); } } dialog_settimeout (timeout,MENU_ESCAPE,false); }else{ xconf_error (MSG_U(E_PROCUPTIME,"Can't parse /proc/uptime\n")); } return timeout; } /* Configure the default runlevel. */ void askrunlevel_config() { char status_graphic[2000]; int graphic_ok = xconf_xok(status_graphic); int net_ok = netconf_netok(NULL); int choice = 0; static const char *config_mode = MSG_U(M_DEFBOOT,"Default boot mode"); static const char *config_define = MSG_U(M_RUNLEVELS,"Runlevels"); DIALOG_MENU dia; static const char *menuopt[]={ " ", config_mode, NULL }; dia.new_menuitems (menuopt); module_setmenu (dia,MENU_BOOT); if (!distrib_isenhanced()){ dia.new_menuitem (MSG_U(M_DEFINE,"Define"), config_define); } while (1){ MENU_STATUS code = dia.editmenu ( MSG_U(T_BOOTCONF,"Boot configuration") ,MSG_U(I_BOOTCONF,"You are allowed to define the default\n" "boot mode of this computer") ,help_boot ,choice,0); if (code != MENU_OK){ break; }else{ const char *key = dia.getmenustr(choice); module_domenu (MENU_BOOT,key); RUNLEVELS runlevels (graphic_ok,net_ok); if (key == config_mode){ runlevels.config(); }else if(key == config_define){ runlevels.define(); } } } } static int askrunlevel_chkterm () { int ret = -1; const char *pt = getenv ("TERM"); if (pt != NULL){ char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"/usr/lib/terminfo/%c/%s",*pt,pt); if (file_exist (path)){ ret = 0; }else{ snprintf (path,sizeof(path)-1,"/usr/share/terminfo/%c/%s",*pt,pt); if (file_exist (path)) ret = 0; } } return ret; } static void askrunlevel_setterm() { /* #Specification: askrunlevel / terminal type askrunlevel is called very early at boot time. At this time the TERM environnement variable is set directly by the kernel (it can be overriden by init I think). In kernel 1.2.x, it is generally set to "con80x25". In newer kernel, it is set to "linux". Many (Most) system out there simply do not have a definition in /usr/lib/terminfo for such a TERM type. When starting askrunlevel, we check if the TERM variable do point to something in /usr/lib/terminfo. If not, TERM is silently set to "linux". If linux is not defined, it is set to "console". This should cover most cases. */ if (askrunlevel_chkterm()==-1){ putenv ("TERM=linux"); if (askrunlevel_chkterm()==-1){ putenv ("TERM=console"); if (askrunlevel_chkterm()==-1){ RUNLEVELS runlevels(0,0); runlevels.setlevel (4); printf (MSG_U(E_TERMINFO ,"No valid TERM definition\r\n" "probably caused by an improper\r\n" "terminfo database.\r\n")); exit (-1); } } } } static bool askrunlevel_wait (int delay) { bool stopped = false, end = false; printf ("\n"); printf (MSG_U(I_ANYKEYINTRO ,"Hit any key to enter Linuxconf configuration mode\r\n" "Hit to resume the boot sequence\r\n")); for (int i=delay; i>0 && ! end; i--){ fd_set f; struct timeval tt; printf ("\r"); printf (MSG_U(I_ANYKEY,"The boot sequence will resume in %2d seconds"),i); fflush (stdout); tt.tv_sec = 1; tt.tv_usec = 0; FD_ZERO (&f); FD_SET (0,&f); initscr(); cbreak(); noecho(); int ret = select (1,&f,NULL,NULL,&tt); if (ret > 0){ char buf[10]; read (0,buf,10); if (buf[0] != '\r') stopped = true; end = true; } endwin(); } printf ("\n"); return stopped; } static void askrunlevel_menu (ASK_PARM &parm, int timeout) { int choice = parm.defmode; while (1){ static const char *set_config = MSG_U(M_TWORKSTATION,"the workstation"); static const char *boot_log = MSG_U(M_BOOTLOGS,"the boot logs"); static const char *ver_config = MSG_U(M_VERCONFIG,"configuration version"); static const char *menuopt1[]={ MSG_U(M_START,"Start"), NULL, " ", NULL, " ", NULL, " ", NULL, " ", NULL, " ", NULL, NULL }; static const char *menuopt2[]={ MSG_U(M_CONFIG,"Configure"), set_config, MSG_U(M_SELECT,"Select"),ver_config, MSG_U(M_VIEW,"View"), boot_log, NULL }; char status_graphic[2000]; int graphic_ok = xconf_xok(status_graphic); char status_net[2000]; int net_ok = netconf_netok(status_net); RUNLEVELS runlevels (graphic_ok,net_ok); int nbmenu = runlevels.setmenu (menuopt1); menuopt1[nbmenu*2] = NULL; char infohelp[500]; int infolen = snprintf (infohelp,sizeof(infohelp)-1 ,MSG_U(I_SELONE ,"Select one of the operation mode below\n" "or configure the default mode.\n" "Current system profile version is '%s'.") ,confver_getcur()); if (timeout != 0){ snprintf (infohelp+infolen,sizeof(infohelp)-infolen-1 ,MSG_U(I_WARN,"\n" "Unless you select something within %d seconds\n" "\"%s\" will start automaticly") ,timeout,menuopt1[choice*2+1]); } timeout = 0; char title[80]; extern char *revision; #if LINUXCONF_SUBSUBREVISION > 0 sprintf (title,"Linuxconf %sR%d-%d: %s" ,revision,LINUXCONF_SUBREVISION,LINUXCONF_SUBSUBREVISION ,MSG_U(T_OPERMODE,"Operation mode")); #elif LINUXCONF_SUBREVISION > 0 sprintf (title,"Linuxconf %sR%d: %s" ,revision,LINUXCONF_SUBREVISION ,MSG_R(T_OPERMODE)); #else sprintf (title,"Linuxconf %s: %s" ,revision,MSG_R(T_OPERMODE)); #endif DIALOG_MENU dia; dia.new_menuitems (menuopt1); dia.new_menuitems (menuopt2); MENU_STATUS code = dia.editmenu (title ,infohelp ,help_askrun ,choice,0); if (code != MENU_OK){ runlevels.setlevel(parm.defmode); break; }else{ const char *key = dia.getmenustr(choice); if (key == set_config){ if (perm_checkpass()){ dialog_mayuselynx(true); linuxconf_main(true); askrunlevel_readparm (parm); dialog_mayuselynx(false); } }else if (key == ver_config){ if (perm_checkpass()){ dialog_mayuselynx(true); confver_selnewver(); dialog_mayuselynx(false); } }else if (key == boot_log){ boot_showlog(); }else{ RUNLEVEL *ptrun = runlevels.getfromchoice(choice); if (!ptrun->graphic_err && !ptrun->net_err){ if (choice == parm.defmode || perm_checkpass()){ runlevels.setlevel(choice); break; } }else if (ptrun->graphic_err && ptrun->net_err){ askrunlevel_saynographic_net(status_graphic,status_net); }else if (ptrun->graphic_err){ askrunlevel_saynographic(status_graphic); }else{ askrunlevel_saynonet(status_net); } } } } } static void askrunlevel_selectprofile() { const char *prof = getenv ("PROFILE"); if (prof != NULL){ FILE_CFG *fout = f_profile.fopen ("w"); if (fout != NULL){ fprintf (fout,"export PROFILE=%s\n",prof); fclose (fout); } confver_selectprofile (prof); } } static MESSAGE_DEF bootcleanup ("bootcleanup"); int askrunlevel_main (int , char *[]) { dialog_mayuselynx(false); /* #Specification: askrunlevel / /var/run/linuxconf.booting The askrunlevel utility create the file /var/run/linuxconf.booting. Later, when linuxconf will continue the boot process, it will use this file as a flag to find out if it is currently "booting" or simply updating the configuration. It allows linuxconf to select the proper title for its logging. */ creat (VAR_RUN_BOOTING,0600); modules_dummy(); // Just to ease the link modlist_dummy(); // same thing askrunlevel_setterm(); /* Specification: askrunlevel / principal The user must select one of these choice Start in graphic mode graphic mode and network text mode text mode and network maintenance mode Configure the workstation Switch the configuration version View boot log */ ASK_PARM parm; askrunlevel_readparm (parm); boot_save2log(); /* Specification: askrunlevel / time & cmos askrunlevel grab the time from cmos right at boot time. The /sbin/clock command in most /etc/rc.d/rc.S is useless. */ dialog_settimeout (parm.diatimeout,MENU_ESCAPE,true); net_introlog (NETINTRO_PREBOOTING); datetime_getfromcmos(); askrunlevel_selectprofile(); linuxconf_setkeymap(); modules_check(); dropin_reenable(); fstab_check(); // Check /etc/fstab fstab_checkmount(true); // mount local filesystems configf_booterase(); // some cleanup module_sendmessage(bootcleanup,0,NULL); fixperm_check_boot(); // check some permissions dropin_doboot(); if (parm.askrun){ dialog_settimeout (-1,MENU_ESCAPE,false); int timeout = askrunlevel_enabletimeout (parm); if (askrunlevel_wait(timeout)){ askrunlevel_menu(parm,0); }else{ RUNLEVELS runlevels (0,0); runlevels.setlevel(parm.defmode); } } return 0; }