#include #include #include #include "managerpm.h" #include "managerpm.m" #include <../../libmodules/filesystem/filesystem.h> #include #include #include #include #include #include #include static const char K_MANAGERPM[]="managerpm"; static const char K_DIRS[]="dirs"; static HELP_FILE help_updatedir ("managerpm","updatedir"); static HELP_FILE help_updatepkg ("managerpm","updatepkg"); static HELP_FILE help_managerpm ("managerpm","managerpm"); static HELP_FILE help_prefs ("managerpm","prefs"); static HELP_FILE help_addons ("managerpm","addons"); static HELP_FILE help_search ("managerpm","search"); static HELP_FILE help_mount ("managerpm","mount"); static HELP_FILE help_uninstall ("managerpm","uninstall"); static HELP_FILE help_install ("managerpm","install"); /* Execute the RPM command and report error messages Return the rpm error code (0 if everything was fine). */ int mngrpm_execrpm ( const SSTRING &args, SSTRINGS &tb) // Will hold stdout { int ret = -1; SSTRING err; POPEN pop ("rpm",args.get()); if (pop.isok()){ while (pop.wait(30) > 0){ char buf[1000]; while (pop.readout(buf,sizeof(buf)-1)!=-1){ tb.add (new SSTRING(buf)); } while (pop.readerr(buf,sizeof(buf)-1)!=-1){ err.append ("\n"); err.append (buf); } } if (!err.is_empty()){ xconf_error (MSG_U(E_RPM,"rpm %s\nreported those errors:") ,err.get()); } ret = pop.getstatus(); } return ret; } PUBLIC RPM_OPTIONS::RPM_OPTIONS() { nodep = false; force = false; oldrev = false; replace = false; noscripts = false; notrigger = false; excludedocs = false; test = false; } static void mngrpm_addargif (SSTRING &args,bool opt, const char *optstr) { if (opt){ args.append (" "); args.append (optstr); } } PUBLIC void RPM_OPTIONS::addargs( SSTRING &args) const { mngrpm_addargif (args,nodep,"--nodeps"); mngrpm_addargif (args,force,"--force"); mngrpm_addargif (args,oldrev,"--oldpackages"); mngrpm_addargif (args,replace,"--replacefiles"); mngrpm_addargif (args,noscripts,"--noscripts"); mngrpm_addargif (args,notrigger,"--notriggers"); mngrpm_addargif (args,excludedocs,"--excludedocs"); mngrpm_addargif (args,test,"--test"); } static char *fsroot = NULL; /* Setup the --root rpm option. This allows installation into a sub-installation. Useful, if managerpm is used from a maintenance disk for example. */ void mngrpm_setfsroot (const char *root) { free (fsroot); fsroot = NULL; if (root != NULL){ fsroot = strdup(root); } } static void mngrpm_setrootopt(char rootopt[PATH_MAX]) { rootopt[0] = '\0'; if (fsroot != NULL){ snprintf (rootopt,PATH_MAX-1,"--root %s",fsroot); } } /* Set a path according to the current root */ void mngrpm_setpath (const char *path, char *abspath, int size) { snprintf (abspath,size-1,"%s%s" ,fsroot != NULL ? fsroot : "" ,path); } /* Install/upgrade/erase many package with an interactive progress bar */ int mngrpm_domany ( const PACKAGES &pkgs, const char *title, const char *rpmoper, const RPM_OPTIONS &options, bool showinfo) { char rootopt[PATH_MAX]; mngrpm_setrootopt (rootopt); SSTRING args; args.setfromf ("%s %s",rootopt,rpmoper); options.addargs(args); int n = pkgs.getnb(); net_introlog (NETINTRO_MISC); net_title (title); net_prtlog (NETLOG_CMD,"rpm %s\n",args.get()); int nbpkg = 0; for (int i=0; iselected){ args.append (" "); const char *pkg = p->fullname.get(); if (pkg[0] == '\0') pkg = p->name.get(); // An uninstall args.append (pkg); net_prtlog (NETLOG_CMD," %s\n",pkg); nbpkg++; } } int ret = -1; if (perm_rootaccess (MSG_U(P_MNGPKG,"to manage packages"))){ DAEMON_INTERNAL *dae = daemon_find ("rpm"); if (dae != NULL){ POPEN pop ("rpm",args.get()); if (pop.isok()){ DIALOG diastatus; diastatus.settype (DIATYPE_POPUP); diastatus.set_formparms ("htrigger=700"); SSTRING curpkg(MSG_U(I_PREPARING,"Preparing")),summary; diastatus.newf_str (MSG_U(F_PACKAGE,"Package"),curpkg); diastatus.set_lastreadonly(); diastatus.newf_str ("",summary,80); diastatus.set_lastreadonly(); int gauge = 0,installed=0; bool preparing = true; diastatus.newf_gauge ("",gauge,100); diastatus.newf_gauge ("",installed,nbpkg); int nof = 0; diastatus.show (title,"",help_none,nof,0); diagui_flush(); DIALOG dia; dia.settype (DIATYPE_NOTICE); while (pop.wait(1000000)!=-1){ char buf[1000]; while (pop.readout(buf,sizeof(buf)-1) != -1){ bool reload_gauge = false; bool reload_pkg = false; if (buf[0] == '%' && buf[1] == '%'){ int new_gauge = atoi(buf+3); if (new_gauge != gauge){ gauge = new_gauge; reload_gauge = true; } }else{ char *pt = strchr(buf,'-'); if (pt != NULL) *pt = '\0'; curpkg.copyword(buf); PACKAGE *cur = pkgs.locate(curpkg.get()); if (cur != NULL){ summary.setfrom (cur->summary); diastatus.reload (1); reload_pkg = true; gauge = 0; reload_gauge = true; } if (preparing){ preparing = false; }else{ installed++; diastatus.reload(3); } } if (reload_gauge || reload_pkg){ if (reload_pkg) diastatus.reload(0); if (reload_gauge) diastatus.reload(2); diastatus.show (title,"",help_none,nof,0); diagui_flush(); } } while (pop.readerr (buf,sizeof(buf)-1)!=-1){ net_prtlog (NETLOG_ERR,buf); strip_end (buf); dia.newf_info ("",buf); } } diastatus.hide(); diagui_flush(); ret = pop.getstatus() ? -1 : 0; if (showinfo || ret != 0){ if (dia.getnb() != 0){ int nof = 0; dia.edit (MSG_U(N_RPMRES,"Rpm results") ,"",help_nil,nof,MENUBUT_CANCEL); }else{ xconf_notice (MSG_U(N_DONE,"Done")); } } } } } return ret; } void mngrpm_setdelopt ( DIALOG &dia, RPM_OPTIONS &options) { dia.newf_chk ("",options.nodep,MSG_R(F_NODEPS)); dia.newf_chk ("",options.force,MSG_R(F_FORCE)); dia.newf_chk ("",options.noscripts,MSG_R(F_NOSCRIPTS)); dia.newf_chk ("",options.notrigger,MSG_R(F_NOTRIGGER)); dia.newf_chk ("",options.test,MSG_R(F_TESTONLY)); } /* Un-install the selected packages. Let the user pick un-install options return -1 if any errors */ PUBLIC int PACKAGES::uninstall() { int ret = -1; if (any_selected()){ bool nodeps = false; if (testuninstall(nodeps)){ DIALOG dia; dia.settype (DIATYPE_POPUP); RPM_OPTIONS options; options.nodep = nodeps ? 1 : 0; mngrpm_setdelopt (dia,options); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_UNINSTPKG,"Uninstall package") ,"",help_uninstall,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ if (mngrpm_domany (*this,MSG_U(T_DELPKGS,"Erase packages") ,"-ev",options,true)==0){ ret = 0; break; } } } } }else{ xconf_error (MSG_U(E_NOSELECTED,"No packages selected")); } return ret; } void mngrpm_setinstopt (DIALOG &dia, RPM_OPTIONS &options) { dia.newf_chk ("",options.nodep,MSG_R(F_NODEPS)); dia.newf_chk ("",options.force,MSG_R(F_FORCE)); dia.newf_chk ("",options.replace,MSG_R(F_REPLACE)); dia.newf_chk ("",options.noscripts,MSG_R(F_NOSCRIPTS)); dia.newf_chk ("",options.notrigger,MSG_R(F_NOTRIGGER)); dia.newf_chk ("",options.excludedocs,MSG_R(F_EXCLUDEDOCS)); dia.newf_chk ("",options.test,MSG_R(F_TESTONLY)); } /* Install the selected packages. Let the user pick install options return -1 if any errors */ PUBLIC int PACKAGES::install() { int ret = -1; if (any_selected()){ DIALOG dia; dia.settype (DIATYPE_POPUP); RPM_OPTIONS options; mngrpm_setinstopt(dia,options); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_INSTPKG,"Install package") ,"",help_install,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ if (mngrpm_domany (*this,MSG_U(T_INSTPKGS,"Install packages") ,"-iv --percent",options,true)==0){ ret = 0; break; } } } }else{ xconf_error (MSG_R(E_NOSELECTED)); } return ret; } PUBLIC void PACKAGES::unselectall() { int n = getnb(); for (int i=0; iselected = 0; } } PUBLIC void PACKAGES::selectall() { int n = getnb(); for (int i=0; iselected = 1; } } /* Is there any package selected in the table */ PUBLIC bool PACKAGES::any_selected() { bool ret = false; int n = getnb(); for (int i=0; iselected){ ret = true; break; } } return ret; } enum STATE { none, provides, requires, list }; class PARSE_STATE{ public: bool is_installed; const char *title; DIALOG dia; STATE state; PACKAGE *curpkg; int nbloaded; int nbpkg; PARSE_STATE(int _nbpkg, bool _is_installed, const char *_title); }; PARSE_STATE::PARSE_STATE( int _nbpkg, bool _is_installed, const char *_title) { is_installed = _is_installed; nbpkg = _nbpkg; title = _title; state = none; curpkg = NULL; nbloaded = 0; dia.settype (DIATYPE_POPUP); char tmp[1000]; dia.gui_passthrough (P_Label,"%s",diagui_quote(_title,tmp)); dia.newline(); dia.newf_gauge (NULL,nbloaded,nbpkg); dia.gui_dispolast (GUI_H_CENTER,1,GUI_V_CENTER,1); int nof = 0; dia.show (title,"",help_none,nof,0); diagui_flush(); } PRIVATE void PACKAGES::parse ( PARSE_STATE &st, char *buf) { strip_end (buf); // fprintf (stderr,"buf :%s:\n",buf); if (buf[0] == '#'){ if (buf[1] == '#'){ char tb[7][100]; const int sizetb = sizeof(tb)/sizeof(tb[0]); tb[6][0] = '\0'; if (str_splitline(buf+2,'\t',tb,sizetb)>=sizetb-1){ for (int i=0; iinstalled = st.is_installed; st.state = requires; // A package provides itself st.curpkg->addprovide (tb[0],NULL); }else{ fprintf (stderr,"Rejects ;%s:\n",buf); } }else if (buf[1] == '-'){ st.state = provides; }else if (buf[1] == '='){ st.state = list; } }else if (buf[0] != '\0' && strcmp(buf,"(none)")!=0){ if (st.curpkg != NULL){ if (st.state == requires){ /* #Specification: rpm output / hack / rpmlib The RPMs contains requierements about rpm itself in the form of "rpmlib(features) <= version". We ignore that. */ if (strncmp(buf,"rpmlib(",7)!=0) st.curpkg->addrequire (buf); }else if (st.state == provides){ st.curpkg->addprovide (buf,NULL); }else if (st.state == list){ /* #Specification: rpm output / hack Some package do not contains any files. The rpm commands output the string "(contains no files)". There does not seem to be an option to get a raw output. This string is probably translatable, so we can't trigger on this. But a file name starting with an open parenthesis is probably not possible either. So we discard anything starting with an open parenthesis */ if (buf[0] != '('){ char tb[7][100]; const int sizetb = sizeof(tb)/sizeof(tb[0]); tb[6][0] = '\0'; if (str_splitline(buf+2,'\t',tb,sizetb)>=4){ st.curpkg->addprovide (tb[0],tb[3]); } } } } } } /* Load the list of installed packages or packages from some directory */ PUBLIC int PACKAGES::load ( const char *rpmarg, bool is_installed, // Set the is_installed flag int nbpkg, // How many package to load or -1 if not known const char *title, // Title for the gauge dialog SSTRINGS *tberr) // Potentially invalid packages // May be NULL { int ret = -1; SSTRING arg; // We start the queryformat with a \n to make sure ## starts on a new // line. Some package have no files (basesystem) and rpm breaks // the output with a (contains no files), without line feed arg.setfromf ("%s --queryformat" " \"\n##%%{name}\t%%{version}\t%%{release}\t%%{group}\t%%{vendor}\t%%{distribution}\t%%{summary}\n\"" " --requires" " --queryformat \"\n#-\n\"" " --provides" " --queryformat \"\n#=\n\"" " --dump" " --queryformat \"\n\"" ,rpmarg); if (nbpkg == -1) nbpkg = 100; POPEN pop ("rpm",arg.get()); if (pop.isok()){ PARSE_STATE st (nbpkg,is_installed,title); SSTRING errors; while (pop.wait(100) > 0){ char buf[1000]; while (pop.readout(buf,sizeof(buf)-1)!=-1){ parse (st,buf); } // hack: // Some packages may be invalid. rpm generally reports // something like: foo.rpm does not appear to be ... // We do a little hack. We take the first word of every // error line and put it in tberr. The caller uses those // words to discard invalid package. I have not found a clean // to do this, beside using librpm maybe. while (pop.readerr(buf,sizeof(buf)-1)!=-1){ errors.append (buf); if (tberr != NULL){ SSTRING *s = new SSTRING; s->copyword (buf); tberr->add (s); } } } if (errors.is_filled()) xconf_error ("%s",errors.get()); ret = 0; }else{ xconf_error (MSG_U(E_CANTEXEC,"Can't execute the command\nrpm %s") ,arg.get()); } diagui_flush(); return ret; } /* Load the list of installed packages or packages from some directory */ PUBLIC int PACKAGES::load ( const char *rpmarg, bool is_installed, // Set the is_installed flag int nbpkg, // How many package to load or -1 if not known const char *title) // Title for the gauge dialog { return load (rpmarg,is_installed,nbpkg,title,NULL); } /* Load the list of installed packages */ PUBLIC int PACKAGES::loadinstall() { char rootopt[PATH_MAX]; mngrpm_setrootopt(rootopt); SSTRING s; s.setfromf ("%s -qa",rootopt); return load (s.get(),true,-1 ,MSG_U(T_LOADINGINSTPKG,"Loading installed packages information")); } /* Convert a pattern into a regex compatible one Support 3 formats -No special character. Some special character are inserted and appended so that we get an exact match. -simple "shell like" pattern. */ static void mngrpm_cnvpattern (const char *pattern, char *exp) { bool found = false; const char *specials = "*?"; while (*specials != '\0'){ if (strchr(pattern,*specials)!=NULL)found = true; specials++; } *exp++ = '#'; if (!found){ exp = stpcpy (exp,pattern); }else{ while (*pattern != '\0'){ char carac = *pattern++; if (carac == '*'){ *exp++ = '.'; *exp++ = '*'; }else if (carac == '?'){ *exp++ = '.'; }else{ *exp++ = carac; } } } *exp++ = '#'; *exp = '\0'; } /* Extract some package using a pattern into another package list. Return the number of packages selected. */ PUBLIC int PACKAGES::select( const char *pattern, PACKAGES &sels) { int ret = 0; sels.neverdelete(); if (pattern[0] == '\0'){ for (int i=0; iname.get(); int lenname = strlen(name); char bufname[lenname+3]; // Put delimiters in place (beginning and end) so // we can do an exact match bufname[0] = '#'; strcpy (bufname+1,name); bufname[lenname+1] = '#'; bufname[lenname+2] = '\0'; regmatch_t tbm[10]; if (regexec (®,bufname,10,tbm,0)!=REG_NOMATCH){ sels.add (p); ret ++; } } regfree (®); } } return ret; } /* Remove packages with the same name. Keep the "newest" one As a side effect, the packages will be sorted */ PUBLIC void PACKAGES::remove_dups() { sort(); int n = getnb()-1; for (int i=0; iname.cmp(n->name)==0){ remove_del (p); i--; n--; } } } /* Load the information about a single package */ PUBLIC int PACKAGES::loadfrompath (const char *path) { char arg[2*PATH_MAX]; snprintf (arg,sizeof(arg)-1,"-qp %s",path); int ret = load (arg,false,-1 ,MSG_U(T_LOADINGPKG,"Loading packages information")); if (ret != -1){ remove_dups(); if (getnb () > 0){ ((PACKAGE *) getitem (0))->fullname.setfrom (path); } } return ret; } /* Load the information about many packages */ PRIVATE int PACKAGES::loadfromdir_real( const char *dir, SSTRINGS &tb) // Invalid package may be removed { int ret = 0; if (tb.getnb() > 0){ SSTRING arg ("-qp"); for (int i=0; iget()); } SSTRINGS tberr; ret = load (arg.get(),false,tb.getnb(),MSG_R(T_LOADINGPKG),&tberr); for (int i=0; iget(); int no = tb.lookup(ivld); if (no != -1) tb.remove_del (no); } } return ret; } #define PRECOMP_VERSION 1 /* Check the version of a precomp file. Return the number of package it contains */ static int check_version (FILE *fin) { int ret = -1; char buf[100]; if (fgets(buf,sizeof(buf)-1,fin)!=NULL && strncmp(buf,"VERSION=",8)==0 && atoi(buf+8)==PRECOMP_VERSION && fgets(buf,sizeof(buf)-1,fin)!=NULL && strncmp(buf,"NBPKG=",6)==0){ ret = atoi(buf+6); } return ret; } /* Load the information about many packages either from the package or a pre-computed file. */ PUBLIC int PACKAGES::loadfromdir(const char *dir, const char *wild) { int ret = -1; SSTRINGS tb; { char cmd[PATH_MAX]; snprintf (cmd,sizeof(cmd)-1,"/bin/ls %s/%s",dir,wild); POPEN pop (cmd); if (pop.isok()){ while (pop.wait(10)>0){ char buf[PATH_MAX]; while (pop.readout(buf,sizeof(buf)-1)!=-1){ strip_end (buf); tb.add (new SSTRING (buf)); } } } } int oldnb = getnb(); if (strcmp(wild,"*.rpm")==0){ // Check if there is a pre-compute informatin file about the // package char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/pkg.precomp",dir); FILE *fin = fopen (path,"r"); if (fin != NULL){ int nbpkg = check_version(fin); if (nbpkg != -1){ PARSE_STATE st (nbpkg,false,MSG_R(T_LOADINGPKG)); char buf[1000]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ parse (st,buf); } ret = 0; } fclose (fin); }else{ ret = loadfromdir_real (dir,tb); } }else{ ret = loadfromdir_real (dir,tb); } // fill the full path of each package as a package may have // a different filename int j=0; for (int i=oldnb; ifullname.setfrom (tb.getitem(j)->get()); // fprintf (stderr,"DIR %s %s\n",p->name.get(),p->fullname.get()); } diagui_flush(); // Make sure the progress gauge goes away return ret; } PUBLIC PACKAGE *PACKAGES::getitem(int no) const { return (PACKAGE*)ARRAY::getitem(no); } PUBLIC PACKAGE *PACKAGES::locate (const char *name) const { PACKAGE *ret = NULL; int n = getnb(); for (int i=0; iname.cmp(name)==0){ ret = p; break; } } return ret; } static int cmp_by_name_version(const ARRAY_OBJ *p1, const ARRAY_OBJ *p2) { PACKAGE *a1 = (PACKAGE*)p1; PACKAGE *a2 = (PACKAGE*)p2; return a1->cmp(a2); } PUBLIC void PACKAGES::sort() { ARRAY::sort(cmp_by_name_version); } /* Build the dialog to show which package must be updated. (Note that the dialog is optional) Set the selected flag for the packages which are expected to be updated (not already installed, newer than the already installed and the newest in case newpkgs contains several versions of the same package. */ static void mngrpm_setdefaultupd( PACKAGES &installed, PACKAGES &newpkgs, bool showdup, // Present all version of a package instead // of only the newest one bool showold, // Present package which are older than the one // installed bool shownew, // Present packages which are not installed // currently DIALOG *dia, // Optional dialog to fill. PACKAGES &shown) // List of package presented in the dialog { int nbnew = newpkgs.getnb(); bool tbskip[nbnew]; memset (tbskip,0,sizeof(tbskip)); // Remove or unselect multiple version of the same package. // Keep only the newest for (int i=1; iname.cmp(prev->name)==0){ if (showdup){ prev->selected = 0; }else{ tbskip[i-1] = true; } } } shown.neverdelete(); // We collect in shown so we can set or reset all collected // package using extra buttons in the dlalog if (dia != NULL){ dia->newf_title (MSG_U(T_PKGLST,"Packages"),1,"",MSG_R(T_PKGLST)); } for (int i=0; iselected = 0; }else{ const char *pname = newp->name.get(); PACKAGE *oldp = installed.locate (pname); if (oldp != NULL){ int vercmp = newp->cmp(oldp); if (vercmp > 0 || (vercmp < 0 && showold)){ char updstr[100]; snprintf (updstr,sizeof(updstr)-1,"%s-%s -> %s-%s %s" ,oldp->version.get(),oldp->release.get() ,newp->version.get(),newp->release.get() ,vercmp < 0 ? MSG_U(I_DOWNGRADE,"*** Downgrading ***") : ""); if (dia != NULL){ dia->newf_chk (pname,newp->selected,updstr); } shown.add (newp); }else{ newp->selected = 0; } }else if (shownew){ char updstr[100]; snprintf (updstr,sizeof(updstr)-1,"%s -> %s" ,MSG_U(F_NOTINSTALLED,"Not installed") ,newp->version.get()); if (dia != NULL){ dia->newf_chk (pname,newp->selected,updstr); } shown.add (newp); }else{ newp->selected = 0; } } } } /* Set the selected flag for the packages which are expected to be updated (not already installed, newer than the already installed and the newest in case newpkgs contains several versions of the same package. This function assumes that newpkgs contains some selected packages. It will remove those unneeded(unselect them). Return the number of selected packages. */ int mngrpm_setdefaultupd( PACKAGES &installed, PACKAGES &newpkgs, bool showdup, // Present all version of a package instead // of only the newest one bool showold, // Present package which are older than the one // installed bool shownew) // Present packages which are not installed // currently { PACKAGES shown; mngrpm_setdefaultupd (installed,newpkgs,showdup,showold,shownew ,NULL,shown); int ret = 0; for (int i=0; iis_selected()) ret++; } return ret; } /* Extract the list of package to update and let the user pick the one he wants. Return -1 if any error or the user cancelled the operation. */ int mngrpm_selectupd ( PACKAGES &installed, PACKAGES &newpkgs, bool showdup, // Present all version of a package instead // of only the newest one bool showold, // Present package which are older than the one // installed bool shownew, // Present packages which are not installed // currently RPM_OPTIONS &options) { int ret = -1; newpkgs.selectall(); DIALOG dia; PACKAGES shown; mngrpm_setdefaultupd (installed,newpkgs,showdup,showold,shownew ,&dia,shown); if (shown.getnb()==0){ xconf_notice (MSG_U(N_NOUPDATENEEDED ,"No package to update.\n%d packages compared!") ,newpkgs.getnb()); }else{ dia.newf_title (MSG_U(T_RPMOPT,"Options"),1,"",MSG_R(T_RPMOPT)); dia.newf_chk ("",options.nodep,MSG_U(F_NODEPS,"No dependency checking (--nodeps)")); dia.newf_chk ("",options.force,MSG_U(F_FORCE,"Force update (--force)")); dia.newf_chk ("",options.oldrev,MSG_U(F_OLDREV,"Allow downgrade (--oldpackages)")); dia.newf_chk ("",options.replace,MSG_U(F_REPLACE,"Replace files from other pkgs (--replacefiles)")); dia.newf_chk ("",options.noscripts,MSG_U(F_NOSCRIPTS,"Do not exec scripts (--noscripts)")); dia.newf_chk ("",options.notrigger,MSG_U(F_NOTRIGGER,"Do not execute triggers (--notriggers)")); dia.newf_chk ("",options.excludedocs,MSG_U(F_EXCLUDEDOCS,"Do not install documentation (--excludedocs)")); dia.newf_chk ("",options.test,MSG_U(F_TESTONLY,"Test, do not install (--test)")); dia.setbutinfo (MENU_USR1,MSG_U(B_NONE,"None"),MSG_R(B_NONE)); dia.setbutinfo (MENU_USR2,MSG_U(B_ALL,"All"),MSG_R(B_ALL)); int nol = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_LISTUPDATE ,"Packages to update") ,MSG_U(I_LISTUPDATE,"Uncheck the packages you do not wish to update") ,help_updatedir ,nol,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1|MENUBUT_USR2); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_USR1 || code == MENU_USR2){ char selected = code == MENU_USR1 ? 0 : 1; for (int i=0; iselected = selected; } dia.reload(); }else if (code == MENU_ACCEPT){ ret = 0; break; } } } return ret; } /* Update many package after checking that it may succeed (dependency testing). */ int mngrpm_doupdate ( PACKAGES &newpkgs, RPM_OPTIONS &options, bool showinfo) { int ret = -1; bool nodeps = options.nodep==1,replacefiles=false; nodeps = options.nodep != 0; if (newpkgs.testinstall(nodeps,replacefiles)){ if (nodeps) options.nodep = 1; if (replacefiles) options.replace = 1; ret = mngrpm_domany (newpkgs,MSG_U(T_UPDPKG,"Updating packages") ,"-Uv --percent",options,showinfo); } return ret; } /* Get the list of predefined (preference) directories for package If none are defined, the path of the RPMS on the distribution CD is used. */ static void mngrpm_getpkgdir(SSTRINGS &tb) { linuxconf_getall (K_MANAGERPM,K_DIRS,tb,true); if (tb.getnb()==0){ const char *path = configf_lookuppath("rpms_on_cdrom"); tb.add (new SSTRING(path)); } } static void mngrpm_setdircombo (FIELD_COMBO *comb) { SSTRINGS tb; mngrpm_getpkgdir(tb); for (int i=0; iaddopt (tb.getitem(i)->get()); } } /* Setup a DIALOG field to pick a directory path. Sets a help list of known directories defined in the preference screen. */ void mngrpm_setdirfield (DIALOG &dia, SSTRING &dir) { FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_UPDATEDIR ,"Directory containing rpms"),dir); mngrpm_setdircombo (comb); dia.last_noempty(); } #if 0 /* Check if a given directory exist. If it does not exist, try to find out if it is part of an unmounted file system. If this is the case, let the user mount it. Return true if the directory is available mpoint will contain the mount points if a mount was done. mngrpm_unmountif() must be called by the client to do the unmount if needed. */ bool mngrpm_ismounted (const char *path, SSTRING &mpoint, const char *cdtitle) { bool ret = true; FSTAB fstab; int lenpath = strlen (path); FSTAB_ENTRY *found = NULL; for (int i=0; iis_valid()){ const char *mpt = ent->getmpoint(); int len = strlen(mpt); if (strcmp(path,mpt)==0){ // Exact match found = ent; break; }else if (lenpath > len && strncmp(path,mpt,len)==0 && path[len] == '/'){ found = ent; break; } } } if (found != NULL && !found->is_mounted()){ SSTRING buf; if (cdtitle != NULL && cdtitle[0] != '\0'){ buf.setfromf (MSG_U(I_INSERTCD ,"Please insert CDROM\n\n\t%s"),cdtitle); }else{ buf.setfromf (MSG_U(I_MOUNT ,"The directory %s\n" "is part of an unmounted filesystem\n" "Do you want to mount it ?"),path); if (strcmp(found->getfs(),"iso9660")==0){ buf.append (MSG_U(I_PUTCD,"\n(Do not forget to put the CDrom in)")); } } if (dialog_yesno (MSG_U(Q_MOUNT,"Mounting file system") ,buf.get(),help_mount)==MENU_YES && perm_rootaccess(MSG_U(P_MOUNT,"mount file systems"))){ ret = found->domount() != -1; if (ret) mpoint.setfrom (found->getmpoint()); } } if (ret){ int type = file_type (path,true); /* Follow symlinks. */ if (type == -1){ xconf_error (MSG_U(E_MISSING,"%s does not exist"),path); ret = false; }else if (type != 1){ xconf_error (MSG_U(E_NOTDIR,"%s exists but is not a directory"),path); ret = false; } } return ret; } /* Unmount what was mounted by mngrpm_ismounted() */ void mngrpm_unmountif (SSTRING &mpoint) { if (!mpoint.is_empty()) netconf_system_if ("umount",mpoint.get()); } #endif static void mngrpm_updatedir () { SSTRING dir; SSTRING wild; DIALOG dia; mngrpm_setdirfield(dia,dir); wild.setfrom ("*.rpm"); dia.newf_str (MSG_U(F_PATTERN,"File name pattern"),wild); char shownew = 0,showold = 0, showdup = 0;; dia.newf_chk ("",showold,MSG_U(F_SHOWOLD,"Show older packages")); dia.newf_chk ("",shownew,MSG_U(F_SHOWNEW,"Show uninstalled packages")); dia.newf_chk ("",showdup,MSG_U(F_SHOWDUP,"Show all available versions")); int nof = 0; SSTRING mpoint; // Will contain the mount point if a file // system was mounted RPM_OPTIONS options; while (dia.edit (MSG_U(T_UPDATEDIR,"Updating from a directory") ,"" ,help_updatedir ,nof)==MENU_ACCEPT){ dia.hide(); if (filesystem_ismounted(dir.get(),mpoint,"")){ PACKAGES install,fromdir; if (install.loadinstall() != -1 && fromdir.loadfromdir(dir.get(),wild.get())!=-1){ if (fromdir.getnb()==0){ xconf_error (MSG_U(E_NOPKGDIR,"No package %s in directory %s") ,wild.get(),dir.get()); }else if (mngrpm_selectupd (install,fromdir,showdup ,showold,shownew,options)!=-1){ if (mngrpm_doupdate (fromdir,options,true)!=-1){ break; } } } } } filesystem_unmountif (mpoint); } static void mngrpm_updatepkg () { SSTRING pkg; DIALOG dia; FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_PACKAGEPATH ,"Path of the rpm file"),pkg); mngrpm_setdircombo (comb); dia.last_noempty(); int nof = 0; RPM_OPTIONS options; while (dia.edit (MSG_U(T_UPDATEPKG,"Updating one package") ,"" ,help_updatepkg ,nof)==MENU_ACCEPT){ PACKAGES install,fromdir; if (install.loadinstall() != -1 && fromdir.loadfrompath(pkg.get())!=-1){ if (fromdir.getnb()==0){ xconf_error (MSG_U(E_NOPKG,"No package %s") ,pkg.get()); }else if (mngrpm_selectupd (install,fromdir ,false,true,true,options)!=-1){ if (mngrpm_doupdate (fromdir,options,true) != -1) break; } } } } static void mngrpm_setpref() { SSTRINGS tb; mngrpm_getpkgdir(tb); DIALOG dia; for (int i=0; i<3; i++) tb.add (new SSTRING); for (int i=0; ifullname.is_filled() ? p->fullname.get() : p->name.get(); snprintf (buf,sizeof(buf)-1,"%s %s %s",qopt,name,arg); fprintf (stderr,"rpmarg %s\n",buf); POPEN pop ("rpm",buf); if (pop.isok()){ int nbword = words.getnb(); bool tbmatch[nbword]; memset (tbmatch,0,sizeof(tbmatch)); while (pop.wait(10)>0){ while (pop.readout(buf,sizeof(buf)-1)!=-1){ for (int i=0; iget())!=NULL){ tbmatch[i]=true; } } } while (pop.readerr(buf,sizeof(buf)-1)!=-1){ fprintf (stderr,"%s\n",buf); } } int nbmatch = 0; for (int i=0; i 0){ match = true; } } } } /* Search one area of a package using one --queryformat tag */ static void mngrpm_searchq ( PACKAGE *p, SSTRINGS &words, bool matchall, bool &match, // Will be set to true if there is a match bool condit, // Should we search this criteria const char *qopt, // -q or -qp const char *tag) // Tag for queryformat { char arg[100]; sprintf (arg,"--queryformat \"%%{%s}\\n\"",tag); mngrpm_searcharg (p,words,matchall,match,condit,qopt,arg); } /* Search all package for the keywords. Present the list in a package browser. */ static void mngrpm_search ( PACKAGES &pkgs, SSTRING &words, bool matchall, const char *qopt, bool in_summary, bool in_desc, bool in_files, bool in_requires, bool in_provides) { PACKAGES founds; founds.neverdelete(); SSTRINGS tbwords; { char word[1000]; const char *pt = words.get(); while (1){ pt=str_copyword (word,pt,sizeof(word)-1); if (word[0] == '\0') break; tbwords.add (new SSTRING (word)); } } DIALOG dia; dia.settype (DIATYPE_POPUP); SSTRING pkg; dia.newf_str (MSG_R(F_PACKAGE),pkg); dia.set_lastreadonly(); int sofar = 0; dia.newf_gauge ("",sofar,pkgs.getnb()); int nof = 0; for (int i=0; iname); sofar++; dia.reload(); dia.show (MSG_U(T_SEARCHING,"Searching"),"",help_none,nof,0); diagui_flush(); bool match = false; mngrpm_searchq (p,tbwords,matchall,match,in_summary,qopt,"summary"); mngrpm_searchq (p,tbwords,matchall,match,in_desc,qopt,"description"); mngrpm_searcharg (p,tbwords,matchall,match,in_files,qopt,"-l"); mngrpm_searcharg (p,tbwords,matchall,match,in_requires,qopt,"--requires"); mngrpm_searcharg (p,tbwords,matchall,match,in_provides,qopt,"--provides"); if (match) founds.add (p); } dia.hide(); if (founds.getnb()==0){ xconf_error (MSG_U(E_NOMATCH,"No matching packages found")); }else{ browse_groups (founds,true); } } /* Dialog to setup the search */ static void mngrpm_search (bool in_rpms) { SSTRING dir; SSTRING wild; DIALOG dia; if (in_rpms){ mngrpm_setdirfield(dia,dir); wild.setfrom ("*.rpm"); dia.newf_str (MSG_R(F_PATTERN),wild); dia.last_noempty(); } SSTRING words; dia.newf_str (MSG_U(F_WORDS,"Keyword(s) to search"),words); dia.last_noempty(); char matchall = 0, in_summary = 1, in_desc = 1, in_files = 1; char in_requires = 0, in_provides = 0; dia.newf_chk ("",matchall,MSG_U(F_MATCHALL,"Only pkg matching all keywords")); dia.newf_chk ("",in_summary,MSG_U(F_INSUMMARY,"Search in summary")); dia.newf_chk ("",in_desc,MSG_U(F_INDESC,"Search in description")); dia.newf_chk ("",in_files,MSG_U(F_INFILES,"Search in file list")); dia.newf_chk ("",in_requires,MSG_U(F_INREQUIRES,"Search in \"requires\"")); dia.newf_chk ("",in_provides,MSG_U(F_INPROVIDES,"Search in \"provides\"")); SSTRING mpoint; int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_SEARCH,"Searching packages") ,MSG_U(I_SEARCH ,"You can find packages which contain some keywords\n" "in their description or file list") ,help_search ,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ dia.hide(); if (in_rpms){ if (filesystem_ismounted (dir.get(),mpoint,"")){ PACKAGES fromdir; if (fromdir.loadfromdir(dir.get(),wild.get())!=-1){ if (fromdir.getnb()==0){ xconf_error (MSG_R(E_NOPKGDIR),wild.get(),dir.get()); }else{ mngrpm_search (fromdir,words,matchall,"-qp",in_summary,in_desc ,in_files,in_requires,in_provides); } } } }else{ PACKAGES installed; if (installed.loadinstall()!=-1){ mngrpm_search (installed,words,matchall,"-q" ,in_summary,in_desc ,in_files,in_requires,in_provides); } } } } filesystem_unmountif (mpoint); } /* Present package which are not from the distribution vendor */ void mngrpm_showaddon () { PACKAGES install; install.loadinstall(); /* #Specification: managerpm / showaddon / trickery with bash To find out the list of packages which were not part of the original distribution, we are cheating a bit. We are using the distribution and vendor tags of the bash package as the reference. This is pretty reliable, except if the admin update bash. The real solution would be to have "per distribution" code which would identify this properly. For example, on redhat, it would read the /etc/redhat-release. For now, most distribution do not have a module, so we are relying on both strategy. Note that distribution module are able to override the calls to function like linuxconf_getval() so can easily return the information we need. */ PACKAGE *bash = install.locate ("bash"); const char *distribution = distrib_getval("rpmdistribution"); const char *vendor = distrib_getval("rpmvendor"); if (distribution == NULL) distribution = bash->distribution.get(); if (vendor == NULL) vendor = bash->vendor.get(); install.sort(); int n = install.getnb(); DIALOG_RECORDS dia; dia.newf_head ("",MSG_U(H_PKGDEF,"Name\tVersion\tVendor\tDist.")); int lookup[install.getnb()]; for (int i=0; ivendor.icmp(vendor)!=0 || p->distribution.icmp(distribution)!=0){ char line[200]; snprintf (line,sizeof(line)-1,"%s-%s\t%s\t%s" ,p->version.get(),p->release.get(),p->vendor.get() ,p->distribution.get()); lookup[dia.getnb()-1] = i; dia.new_menuitem (p->name.get(),line); } } if (dia.getnb()>1){ int nof = 0; while (1){ MENU_STATUS code = dia.editmenu (MSG_U(T_ADDONS,"Add-on packages") ,MSG_U(I_ADDONS ,"Here is the list of packages not supplied\n" "in the orignal distribution") ,help_addons,nof,0); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ // Provide info for that package PACKAGE *p = install.getitem(lookup[nof]); if (p != NULL) p->showinfo(); } } } } void mngrpm_menu() { static const char *pref = MSG_U(M_PREFER,"Preferences"); static const char *updatepkg = MSG_U(M_UPDATEPKG,"Install/Update one package"); static const char *updatedir = MSG_U(M_UPDATEDIR,"Install/Update many packages"); static const char *browseins = MSG_U(M_BROWEINS,"Browse installed packages"); static const char *browseunins = MSG_U(M_BROWEUNINS,"Browse non installed packages"); static const char *alien = MSG_U(M_ALIEN,"Show add-on packages"); static const char *search_rpms = MSG_U(M_SEARCH,"Search a package"); static const char *search_installed = MSG_U(M_SEARCHINST,"Search an installed package"); static const char *snapshot = MSG_U(M_SNAPSHOT,"Take a snapshot of the packages"); static const char *tbopt[]={ "", pref, "", updatepkg, "", updatedir, "", browseins, "", browseunins, "", search_rpms, "", search_installed, "", alien, "", snapshot, NULL }; DIALOG_MENU dia; dia.new_menuitems (tbopt); int nof = 0; while (1){ MENU_STATUS code = dia.editmenu (MSG_R(M_managerpm) ,MSG_U(I_managerpm ,"You can inspect/update packages on your system either\n" "one by one or in batch.\n") ,help_managerpm ,nof,0); if (code == MENU_ESCAPE || code == MENU_QUIT){ break; }else if (perm_rootaccess(MSG_R(P_MNGPKG))){ const char *sel = dia.getmenustr(nof); if (sel == pref){ mngrpm_setpref(); }else if (sel == updatepkg){ mngrpm_updatepkg(); }else if (sel == updatedir){ mngrpm_updatedir(); }else if (sel == alien){ mngrpm_showaddon(); }else if (sel == browseins){ browse_installed(); }else if (sel == browseunins){ browse_uninstalled(); }else if (sel == search_rpms){ mngrpm_search(true); }else if (sel == search_installed){ mngrpm_search(false); }else if (sel == snapshot){ xconf_notice ("Sorry not implemented"); } } } }