/* Copyright 1999 Red Hat, Inc. * * This software may be freely redistributed under the terms of the GNU * public license. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _(String) gettext((String)) #define N_(String) (String) #include "kudzu.h" #include "modules.h" #define FILENAME "hwconf" static int madebak=0; static int quiet=0; static int timeout=0; static int safe=0; struct device **storedDevs; int numStored=0; struct device **currentDevs; int numCurrent=0; void startNewt() { char roottext[80]; newtInit(); newtCls(); newtPushHelpLine(_(" / between elements | selects | next screen")); snprintf(roottext, 80, _("Hardware Discovery Utility %s " " (C) 1999 Red Hat, Inc."), VERSION); newtDrawRootText(0, 0, roottext); } void winStatus(int width, int height, char * title, char * text, ...) { newtComponent t, f; char * buf = NULL; int size = 0; int i = 0; va_list args; va_start(args, text); do { size += 1000; if (buf) free(buf); buf = malloc(size); i = vsnprintf(buf, size, text, args); } while (i == size); va_end(args); newtCenteredWindow(width, height, title); t = newtTextbox(1, 1, width - 2, height - 2, NEWT_TEXTBOX_WRAP); newtTextboxSetText(t, buf); f = newtForm(NULL, NULL, 0); free(buf); newtFormAddComponent(f, t); newtDrawForm(f); newtRefresh(); newtFormDestroy(f); } int installXRPM(char *server) { int x,done=0; char path[512]; char *dirname; struct newtWinEntry ne[2]; while (!done) { /* OK, let's play Find the RPM! */ x=newtWinTernary(_("RPM Required"),_("CD"),_("Filesystem"), _("Cancel"), _("The XFree86-%s RPM is needed to use this graphics card." "You can install it from your Red Hat Linux CD-ROM, " "from another location on the filesytem, or postpone " "it until later."), server); switch (x) { case 1: newtWinMessage(_("Insert CD-ROM"),_("Ok"), _("Please insert your Red Hat Linux CD-ROM.")); dirname=strdup("/mnt/kudzuXXXXXX"); x=mkstemp(dirname); close(x); /* This is a race. :( */ unlink(dirname); mkdir(dirname,0755); if (mount("/dev/cdrom",dirname,"iso9660",MS_RDONLY,NULL)) { newtWinMessage(_("Mount failed"),_("Ok"), _("Could not mount CD-ROM.")); unlink(dirname); break; } snprintf(path,512,"/bin/rpm -i %s/XFree86-%s*",dirname,server); newtSuspend(); x=system(path); newtResume(); if (!x) { done=1; openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,"installed XFree86-%s server",server); closelog(); } else { newtWinMessage(_("RPM Install Failed"),_("Ok"), _("RPM install of XFree86-%s failed."),server); } umount(dirname); unlink(dirname); free(dirname); break; case 2: while (1) { dirname=NULL; ne[0].text = strdup(_("Path:")); ne[0].value = &dirname; ne[0].flags = NEWT_FLAG_SCROLL; memset(&ne[1],'\0',sizeof(struct newtWinEntry)); snprintf(path,512,_("Please enter the path where the XFree86-%s RPM can be found:"),server); x=newtWinEntries(_("Enter path to RPM"), path,60,0,0,40, ne,_("Ok"),_("Cancel"),NULL); if (x==2 || x==0) { if (dirname) free(dirname); break; } snprintf(path,512,"/bin/rpm -i %s/XFree86-%s*",dirname,server); newtSuspend(); x=system(path); newtResume(); if (!x) { done=1; free(dirname); openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,"installed XFree86-%s server",server); closelog(); break; } else { newtWinMessage(_("RPM Install Failed"),_("Ok"), _("RPM install of XFree86-%s failed."),server); } } break; case 3: return 1; break; default: return 1; break; } } return 0; } void Xconfig(struct device *dev) { int serverfile; int status; int cardindex=0; struct device *mousedev = NULL; int fd; int x; char path[512]; char buf[40]; char servername[40]; for (x=0 ; currentDevs[x] ; x++) { if (currentDevs[x]->class == CLASS_MOUSE) mousedev = currentDevs[x]; } memset(buf,'\0',40); memset(servername,'\0',40); if (!strncmp(dev->driver,"Card:",5)) { snprintf(path,512,"/usr/X11R6/bin/Xconfigurator --pick --card \"%s\" --kickstart", dev->driver+5); newtSuspend(); system(path); newtResume(); serverfile=open("/tmp/SERVER",O_RDONLY); if (serverfile==-1) return; memset(path,'\0',512); read(serverfile,path,512); close(serverfile); strncpy(buf,path,strstr(path," ")-path); cardindex=atoi(strstr(path," ")+1); } else { strncpy(buf,strstr(dev->driver,":")+1,40); unlink("/tmp/SERVER"); serverfile=open("/tmp/SERVER",O_WRONLY|O_CREAT|O_EXCL); if (serverfile==-1) return; snprintf(path,512,"%s -1\n",buf); write(serverfile,path,strlen(path)); close(serverfile); } snprintf(path,512,"/bin/rpm -q XFree86-%s --qf \"\" 2>/dev/null",buf); newtSuspend(); status = system(path); newtResume(); if (status) { if (installXRPM(buf)) return; } if (mousedev) snprintf(path,512,"/usr/X11R6/bin/Xconfigurator --continue --mouse %s --mousedevice %s", mousedev->driver,mousedev->device); else snprintf(path,512,"/usr/X11R6/bin/Xconfigurator --continue"); newtSuspend(); x=system(path); newtResume(); openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,"ran Xconfigurator for %s",dev->driver); if (x==0) { /* This is a sick and gross hack. */ fd=open("/tmp/ranXconfig",O_CREAT|O_EXCL,0644); if (fd!=-1) close(fd); } closelog(); } char *checkConfFile() { char path[_POSIX_PATH_MAX]; struct stat sbuf; snprintf(path,_POSIX_PATH_MAX,"/etc/sysconfig/%s",FILENAME); if (stat(path,&sbuf)==-1) { snprintf(path,_POSIX_PATH_MAX,"./%s",FILENAME); if (stat(path,&sbuf)==-1) { return NULL; } } return strdup(path); } int makeLink(struct device *dev, char *name) { char oldfname[256],newfname[256]; if (!dev->device || !name) return 1; snprintf(oldfname,256,"/dev/%s",dev->device); if (dev->index > 0) { snprintf(newfname,256,"/dev/%s%d",name,dev->index); } else { snprintf(newfname,256,"/dev/%s",name); } openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,_("linked %s to %s"),newfname,oldfname); closelog(); return symlink(oldfname,newfname); } int removeLink(struct device *dev, char *name) { char newfname[256]; char oldfname[256]; int x; if (!name) return 1; if (dev->index > 0) { snprintf(newfname,256,"/dev/%s%d",name,dev->index); } else { snprintf(newfname,256,"/dev/%s",name); } memset(oldfname,'\0',256); x=readlink(newfname,oldfname,255); openlog("kudzu",0,LOG_USER); if (x!=-1) syslog(LOG_NOTICE,_("unlinked %s (was linked to %s)"),newfname,oldfname); else syslog(LOG_NOTICE,_("unlinked %s"),newfname); closelog(); return(unlink(newfname)); } int isLinked(struct device *dev, char *name) { char path[256],path2[256]; memset(path,'\0',256); memset(path2,'\0',256); if (!name) return 0; if (dev->index) snprintf(path,256,"/dev/%s%d",name,dev->index); else snprintf(path,256,"/dev/%s",name); if (readlink(path,path2,256)>0) { if (!strcmp(basename(path2),dev->device)) return 1; } return 0; } int isConfigured(struct device *dev) { struct confModules *cf; char path[256],path2[256]; struct stat sbuf; int ret=0; memset(path,'\0',256); memset(path2,'\0',256); cf = readConfModules("/etc/conf.modules"); switch (dev->class) { case CLASS_NETWORK: if (cf) if (isAliased(cf,"eth",dev->driver)!=-1) ret = 1; break; case CLASS_SCSI: if (cf) if (isAliased(cf,"scsi_hostadapter",dev->driver)!=-1) ret=1; break; case CLASS_VIDEO: /* Assume on initial runs that if X is configured, we got the right card */ #ifndef __sparc__ if (!stat("/etc/X11/XF86Config",&sbuf)) ret = 1; #else ret = 1; #endif break; case CLASS_AUDIO: if (cf) { if (isAliased(cf,"sound",dev->driver)!=-1) ret = 1; if (isAliased(cf,"sound-slot-",dev->driver)!=-1) ret = 1; } break; case CLASS_MOUSE: ret = isLinked(dev,"mouse"); break; case CLASS_MODEM: ret = isLinked(dev,"modem"); break; case CLASS_CDROM: ret = isLinked(dev,"cdrom"); break; case CLASS_SCANNER: ret = isLinked(dev,"scanner"); break; case CLASS_PRINTER: #ifdef _we_need_a_printer_tool_ /* fairly brainded printcap parser */ { char *buf,*ptr,*tmp; int fd; fd = open("/etc/printcap",O_RDONLY); if (fd==-1) break; fstat(fd,&sbuf); buf=malloc(sbuf.st_size+1); if (read(fd,buf,sbuf.st_size)!=sbuf.st_size) break; buf[sbuf.st_size] = '\0'; ptr=buf; while (buf[0]!='\0') { if (ptr[0] == '#') { while (*ptr && *ptr != '\n') ptr++; if (*ptr) { *ptr='\0'; ptr++; } buf=ptr; continue; } while (*ptr && *ptr != '\0' && *ptr !='\n') ptr++; if (*ptr) { *ptr='\0'; ptr++; } if ((tmp=strstr(buf,"lp=/dev/"))) { while (*tmp && *tmp != ':') tmp++; if (*tmp) { *tmp = '\0'; } if (!strcmp(buf+8,dev->device)) ret = 1; } buf = ptr; } break; } #endif default: /* If we don't know how to configure it, assume it's configured. */ ret = 1; break; } if (cf) freeConfModules(cf); return ret; } int configure(struct device *dev) { newtComponent tb, form; struct confModules *cf; char *tmp; char path[256]; struct stat sbuf; int x,index; if (!quiet) { snprintf(path,256,_("Configuring %s"),dev->desc); winStatus(50,3,_("Configuring"),path); sleep(1); } switch (dev->class) { case CLASS_NETWORK: cf = readConfModules("/etc/conf.modules"); if (!cf) cf = newConfModules(); cf->madebackup = madebak; if (isAliased(cf,"eth",dev->driver)==-1) { index=0; while (1) { snprintf(path,256,"eth%d",index); if (getAlias(cf,path)) index++; else break; } addAlias(cf,path,dev->driver,CM_REPLACE); writeConfModules(cf,"/etc/conf.modules"); madebak = cf->madebackup; openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,_("aliased %s as %s"),path,dev->driver); closelog(); } freeConfModules(cf); if (!quiet) { snprintf(path,256,"/etc/sysconfig/network-scripts/ifcfg-eth%d",dev->index); x=0; if (!stat(path,&sbuf)) { x=newtWinChoice(_("Existing Configuration Detected"),_("Yes"),_("No"), _("Migrate existing network configuration?")); } if (x==2 || x==0) { snprintf(path,256,"/usr/sbin/netconfig --device eth%d",dev->index); newtSuspend(); system(path); newtResume(); openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,_("ran netconfig for eth%d"),dev->index); closelog(); } } break; case CLASS_SCSI: cf = readConfModules("/etc/conf.modules"); if (!cf) cf = newConfModules(); cf->madebackup = madebak; if (isAliased(cf,"scsi_hostadapter",dev->driver)==-1) { index=0; while (1) { if (index) snprintf(path,256,"scsi_hostadapter%d",index); else snprintf(path,256,"scsi_hostadapter"); if (getAlias(cf,path)) index++; else break; } addAlias(cf,path,dev->driver,CM_REPLACE); writeConfModules(cf,"/etc/conf.modules"); madebak = cf->madebackup; openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,_("aliased %s as %s"),path,dev->driver); closelog(); } freeConfModules(cf); break; case CLASS_VIDEO: if (!quiet) { Xconfig(dev); } break; case CLASS_AUDIO: if (!quiet) { newtSuspend(); system("sndconfig"); newtResume(); openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,_("ran soundconfig for %s"),dev->driver); closelog(); } break; case CLASS_MOUSE: makeLink(dev,"mouse"); if (!quiet) { newtSuspend(); snprintf(path,256,"/usr/sbin/mouseconfig --device %s %s",dev->device,dev->driver); system(path); newtResume(); openlog("kudzu",0,LOG_USER); syslog(LOG_NOTICE,_("ran mouseconfig for %s"),dev->device); closelog(); } case CLASS_MODEM: makeLink(dev,"modem"); break; case CLASS_CDROM: makeLink(dev,"cdrom"); break; case CLASS_SCANNER: makeLink(dev,"scanner"); break; case CLASS_PRINTER: #ifdef _we_need_a_printer_tool_ if (!quiet) { newtWinMessage( _("Run printtool"), _("Ok"), _("To properly configure your %s, you need" "to run the 'printtool' command from" "the X window system"), dev->desc); } break; #endif case CLASS_TAPE: case CLASS_FLOPPY: case CLASS_HD: case CLASS_RAID: default: break; } if (!quiet) newtPopWindow(); return 0; } int unconfigure(struct device *dev) { newtComponent tb, form; struct confModules *cf; char path[256]; char *tmpalias; int index,needed; if (!quiet) { snprintf(path,256,_("Unconfiguring %s"),dev->desc); winStatus(50,3,_("Removing Configuration"),path); sleep(1); } switch (dev->class) { case CLASS_NETWORK: cf = readConfModules("/etc/conf.modules"); if (!cf) cf = newConfModules(); cf->madebackup = madebak; index = 0; while (1) { snprintf(path,256,"eth%d",index); tmpalias=getAlias(cf,path); if (tmpalias && !strcmp(tmpalias,dev->driver)) { int x; needed = 0; for (x=0;currentDevs[x];x++) { if (currentDevs[x]->driver && !strcmp(currentDevs[x]->driver,dev->driver)) { needed = 1; break; } } if (!needed) removeAlias(cf,path,CM_REPLACE); } else break; } writeConfModules(cf,"/etc/conf.modules"); madebak = cf->madebackup; freeConfModules(cf); break; case CLASS_SCSI: cf = readConfModules("/etc/conf.modules"); if (!cf) cf = newConfModules(); cf->madebackup = madebak; index = 0; while (1) { if (index) snprintf(path,256,"scsi_hostadapter%d",index); else snprintf(path,256,"scsi_hostadapter"); tmpalias=getAlias(cf,path); if (tmpalias && !strcmp(tmpalias,dev->driver)) { int x; needed = 0; for (x=0;currentDevs[x];x++) { if (currentDevs[x]->driver && !strcmp(currentDevs[x]->driver,dev->driver)) { needed = 1; break; } } if (!needed) removeAlias(cf,path,CM_REPLACE); } else break; } writeConfModules(cf,"/etc/conf.modules"); madebak = cf->madebackup; freeConfModules(cf); break; case CLASS_VIDEO: break; case CLASS_AUDIO: break; case CLASS_MOUSE: removeLink(dev,"mouse"); break; case CLASS_MODEM: removeLink(dev,"modem"); break; case CLASS_CDROM: removeLink(dev,"cdrom"); break; case CLASS_SCANNER: removeLink(dev,"scanner"); break; case CLASS_PRINTER: case CLASS_TAPE: case CLASS_FLOPPY: case CLASS_HD: case CLASS_RAID: default: break; } if (!quiet) newtPopWindow(); return 0; } void showWelcome(int timeout) { int x=timeout; int y=0; struct pollfd pfd; newtComponent textbox, form; char message[2048]; pfd.fd = 0; pfd.events = POLLIN | POLLPRI; if (x) { newtCenteredWindow(60,11,_("Welcome to Kudzu")); textbox = newtTextbox(1,1,58,9,NEWT_TEXTBOX_WRAP); } else { newtCenteredWindow(60,9,_("Welcome to Kudzu")); textbox = newtTextbox(1,1,58,7,NEWT_TEXTBOX_WRAP); } form = newtForm(NULL,NULL,0); newtFormAddComponent(form,textbox); newtDrawForm(form); do { if (x>0) { snprintf(message,2048, _("Welcome to Kudzu, the Red Hat Linux hardware " "detection and configuration tool.\n\n" "On the following screens you will be able to " "configure any new or removed hardware for your " "computer.\n\n" " Press any key to continue.\n\n" " Normal bootup will continue in %d seconds."), x ); } else { snprintf(message,2048, _("Welcome to Kudzu, the Red Hat Linux hardware " "detection and configuration tool.\n\n" "On the following screens you will be able to " "configure any new or removed hardware for your " "computer.\n\n" " Press any key to continue.")); } newtTextboxSetText(textbox,message); newtDrawForm(form); newtRefresh(); y=poll(&pfd,1,1000); if (y>0 && pfd.revents & (POLLIN | POLLPRI)) break; x--; } while (x!=0); if (x==0 && y<=0) { winStatus(60,11,_("Welcome to Kudzu"), _("Welcome to Kudzu, the Red Hat Linux hardware " "detection and configuration tool.\n\n\n\n" " Timeout exceeded.")); sleep(1); newtPopWindow(); newtPopWindow(); newtFinished(); exit(5); } newtPopWindow(); } void configMenu(struct device **oldDevs, struct device **newDevs, int runFirst) { int x, y, z, work, rc; /* First, make sure we have work to do... */ work=0; for (x=0;oldDevs[x];x++) { if (oldDevs[x]->class != CLASS_OTHER) { if (!oldDevs[x]->detached) { work++; } else { /* Add detached devices to current list */ currentDevs = realloc(currentDevs,(numCurrent+2)*sizeof(struct device *)); currentDevs[numCurrent] = oldDevs[x]; currentDevs[numCurrent+1] = NULL; numCurrent++; } } } for (x=0;newDevs[x];x++) { if (newDevs[x]->class != CLASS_OTHER) if (!runFirst || !isConfigured(newDevs[x])) work++; } if (!work) return; /* Now, do it... */ if (!quiet) { startNewt(); showWelcome(timeout); } for (x=0;oldDevs[x];x++) { if (oldDevs[x]->class != CLASS_OTHER && !oldDevs[x]->detached) { if (!quiet) rc=newtWinTernary(_("Hardware Removed"),_("Remove Configuration"), _("Keep Configuration"),_("Do Nothing"), _("The following hardware has been removed from " "your system:\n %s\n\n" "You can choose to:\n\n" "1) Remove any existing " "configuration for the device.\n" "2) Keep the existing configuration. " "You will not be prompted " "again if the device seems to be missing.\n" "3) Do nothing. The configuration will " "not be removed, but if the device is found missing on " "subsequent reboots, you will be prompted again."), oldDevs[x]->desc); else rc=0; switch (rc) { case 0: case 1: unconfigure(oldDevs[x]); break; case 2: case 3: default: if (rc==2) oldDevs[x]->detached = 1; currentDevs = realloc(currentDevs,(numCurrent+2)*sizeof(struct device *)); currentDevs[numCurrent] = oldDevs[x]; currentDevs[numCurrent+1] = NULL; numCurrent++; break; } } } for (x=0;newDevs[x];x++) { if (newDevs[x]->class != CLASS_OTHER && (!runFirst || !isConfigured(newDevs[x]))) { if (!quiet) rc=newtWinTernary(_("Hardware Added"),_("Configure"), _("Ignore"), _("Do Nothing"), _("The following hardware has been added to " "your system:\n %s\n\n" "You can choose to:\n\n" "1) Configure the device.\n" "2) Ignore the device. No configuration will " "be added, but you will not be prompted if " "the device is detected on subsequent reboots.\n" "3) Do nothing. No configuration will be " "added, and the device will show up as new if " "it is detected on subsequent reboots."), newDevs[x]->desc); else rc=0; switch (rc) { case 0: case 1: configure(newDevs[x]); break; case 2: break; case 3: default: y=0; while (currentDevs[y]) { if (currentDevs[y]==newDevs[x]) { for (z=y;z 0) { } if (( rc < -1)) { fprintf(stderr, "%s: %s\n", poptBadOption(context, POPT_BADOPTION_NOALIAS), poptStrerror(rc)); exit(-1); } if (!(confFile=checkConfFile())) { runFirst=1; } initializeDeviceList(BUS_UNSPEC); if (runFirst) { storedDevs = malloc(sizeof(struct device *)); storedDevs[0] = NULL; } else storedDevs = readDevices(confFile); while (storedDevs[numStored]) numStored++; if (safe) currentDevs = probeDevices(CLASS_UNSPEC, BUS_UNSPEC, (PROBE_ALL|PROBE_SAFE)); else currentDevs = probeDevices(CLASS_UNSPEC, BUS_UNSPEC, PROBE_ALL); while (currentDevs[numCurrent]) numCurrent++; ret = listCompare(storedDevs, currentDevs, &oldDevs, &newDevs); if (!ret) { writeDevices(confFile,currentDevs); exit(0); } else { configMenu(oldDevs,newDevs,runFirst); } if (!runFirst) writeDevices(confFile,currentDevs); else writeDevices("/etc/sysconfig/hwconf",currentDevs); newtFinished(); return 0; }