Re: [PATCH 1/7] Backport the driver disc functionality from rhel5

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



"git config --global sendemail.chainreplyto no" might be a good thing to do ;)

On 12/02/2009 11:27 AM, Martin Sivak wrote:

> +/* modprobe DD mode */
> +int modprobeDDmode()
> +{
> +  FILE *f = fopen("/etc/depmod.d/ddmode.conf", "w");
> +  if(f){
> +    struct utsname unamedata;
> +    if(uname(&unamedata)){
> +      fprintf(f, " pblacklist /lib/modules\n");
> +    }
> +    else{
> +      fprintf(f, " pblacklist /lib/modules/%s\n", unamedata.release);
> +    }
> +
> +    fclose(f);
> +  }

What's with the funky coding style? It doesn't match the rest of the file.

> +
> +  return f==NULL;
> +}
> +
> +int modprobeNormalmode()
> +{
> +  unlink("/etc/depmod.d/ddmode.conf");
> +
> +  /* run depmod to refresh modules db */
> +  if(system("depmod -a")){
> +      /* FIXME: depmod didn't run */
> +  }

Seems like there's nothing you can really do here except log it.

> +
> +  return 0;
> +}

What's the point of this not being a void function?

> +
> +/* RPM extraction dependency checks */
> +int dlabelDeps(const char* depends, void *userptr)
> +{
> +  logMessage(DEBUGLVL, "Depends on: %s\n", depends);
> +  return 0;
> +}

What's the point of this at all?

> +
> +int dlabelProvides(const char* dep, void *userptr)
> +{
> +  char *kernelver = (char*)userptr;
> +
> +  logMessage(DEBUGLVL, "Provides: %s\n", dep);
> +
> +  return strcmp(dep, kernelver);
> +}

A better function name and some comments would be nice here.  Upon reading it,
I'm not at all sure what it's supposed to do.

> +
> +int dlabelFilter(const char* name, struct stat *fstat, void *userptr)
> +{
> +  int l = strlen(name);
> +  
> +  logMessage(DEBUGLVL, "Unpacking %s (%lluB)\n", name, fstat->st_size);
> +
> +  /* we want firmware files */
> +  if(!strncmp("lib/firmware/", name, 13)) return 0; 
> +
> +  if(l<3) return 1;
> +  l-=3;
> +
> +  /* and we want only .ko files */
> +  if(strncmp(".ko", name+l, 3)) return 1;

Better to use strcmp() here.

> +
> +  /* TODO we are unpacking kernel module, read it's description */
> +
> +  return 0;
> +}


> +
> +char* moduleDescription(const char* modulePath)
> +{
> +  char *command = rstrscat(NULL, "modinfo --description '", modulePath, "'", NULL);
> +  FILE *f = popen(command, "r");
> +  if(f==NULL) return NULL;
> +  
> +  char *description = malloc(sizeof(char)*256);
> +  int size = fread(description, 1, 255, f);

Couple of things here:
1) All the code/decleration mixing here isn't very easy to read.
2) allocations need to be checked
3) There's a pretty good case for using checked_asprintf() instead of using
   *anything* provided by rpmlib. Or implement a stack-allocating sprintfa(),
   which would be even better since it'll cut down on free()s in the return
   paths. You can borrow this code from nash in mkinitrd (git is at 
   ssh://git.fedoraproject.org/git/hosted/mkinitrd) if you like. I think I
   called it asprintfa() there, but whatever.

> +  if(size==0){
> +    free(description);
> +    return NULL;
> +  }
> +  description[size-1]=0; /* strip the trailing newline */

I'm pretty sure this should be using strrchr() to find the newline rather than
expecting all description strings to be exactly 254 bytes plus a newline.  (a
read/realloc loop also isn't out of the question...)

> +  pclose(f);
> +
> +  free(command);

Why not free this immediately after the popen?

> +  return description;
> +}
> +
> +int globErrFunc(const char *epath, int eerrno)
> +{
> +  return 0;
> +}

Seems like the wrong thing to do - you're effectively telling glob() to ignore
all errors and try to continue processing, but you're not doing anything about
the errors while doing so. I think "return immediately" (i.e. NULL or a
globErrFunc() that returns nonzero) makes more sense if we're not doing any
actual handling of the errors.

> +int dlabelUnpackRPMDir(char* rpmdir, char* destination)
> +{
> +  char *kernelver;
> +  struct utsname unamedata;
> +  char *oldcwd;
> +  char *globpattern;
> +  int rc;
> + 
> +  /* get current working directory */ 
> +  oldcwd = getcwd(NULL, 0);
> +
> +  /* set the cwd to destination */
> +  if(chdir(destination)){
> +    free(oldcwd);
> +    return 1;
> +  }
> +
> +  /* get running kernel version */
> +  if(uname(&unamedata)){
> +    kernelver = rstrscat(NULL, "kernel-modules-", "unknown", NULL);
> +  }
> +  else{
> +    kernelver = rstrscat(NULL, "kernel-modules-", unamedata.release, NULL);
> +  }

rc = uname(&unamedata);
checked_asprintf(&kernelver, "kernel-modules-%s", rc ? "unknown" : unamedata.release);

> +  logMessage(DEBUGLVL, "Kernel version: %s\n", kernelver);
> +
> +  globpattern = rstrscat(NULL, rpmdir, "/*.rpm", NULL);
> +  glob_t globres;
> +  char** globitem;
> +  if(!glob(globpattern, GLOB_NOSORT|GLOB_NOESCAPE, globErrFunc, &globres)){
> +    /* iterate over all rpm files */
> +    globitem = globres.gl_pathv;
> +    while(globres.gl_pathc>0 && globitem!=NULL){
> +      explodeRPM(*globitem, dlabelFilter, dlabelProvides, dlabelDeps, kernelver);

Where does explodeRPM come from? Ah, it's in a later patch. I usually find it
better to organize a patchset like this as:

1) description of the overall goal
2) cleanup and removal of old code
3) addition of helper functions
4) addition of the new stuff that uses the helpers.

> +    }
> +    globfree(&globres);
> +    /* end of iteration */
> +  }
> +  free(globpattern);
> +
> +  /* restore CWD */
> +  if(chdir(oldcwd)){
> +   /* FIXME: too bad.. */
> +  }
> +
> +  /* cleanup */
> +  free(kernelver);
> +  free(oldcwd);
> +  return rc;
> +}
> +
> +
> +static char * driverDiskFiles[] = { "repodata", NULL };
>  
>  static int verifyDriverDisk(char *mntpt) {
>      char ** fnPtr;
>      char file[200];
>      struct stat sb;
>  
> -    for (fnPtr = driverDiskFiles; *fnPtr; fnPtr++) {
> -        sprintf(file, "%s/%s", mntpt, *fnPtr);
> -        if (access(file, R_OK)) {
> -            logMessage(ERROR, "cannot find %s, bad driver disk", file);
> -            return LOADER_BACK;
> -        }
> -    }
> -
> -    /* check for both versions */
> +    /* check for dd descriptor */
>      sprintf(file, "%s/rhdd", mntpt);
>      if (access(file, R_OK)) {
> -        logMessage(DEBUGLVL, "not a new format driver disk, checking for old");
> -        sprintf(file, "%s/rhdd-6.1", mntpt);
> -        if (access(file, R_OK)) {
> -            logMessage(ERROR, "can't find either driver disk identifier, bad "
> -                       "driver disk");
> -        }
> +        logMessage(ERROR, "can't find driver disk identifier, bad "
> +                          "driver disk");
> +        return LOADER_BACK;
>      }
>  
>      /* side effect: file is still mntpt/ddident */
>      stat(file, &sb);
>      if (!sb.st_size)
>          return LOADER_BACK;
> +    for (fnPtr = driverDiskFiles; *fnPtr; fnPtr++) {
> +        sprintf(file, "%s/%s/%s", mntpt, getProductArch(), *fnPtr);

I realize mntpt and getProductArch's return are unlikely to be very large, but
these should still probably be snprintf() or checked_asprintf() (... or sprintfa()
if it were to suddenly come into existence ;)

> +        if (access(file, R_OK)) {
> +            logMessage(ERROR, "cannot find %s, bad driver disk", file);
> +            return LOADER_BACK;
> +        }
> +    }
>  
>      return LOADER_OK;
>  }
> @@ -101,25 +247,20 @@ static void copyErrorFn (char *msg) {
>  /* this copies the contents of the driver disk to a ramdisk and loads
>   * the moduleinfo, etc.  assumes a "valid" driver disk mounted at mntpt */
>  static int loadDriverDisk(struct loaderData_s *loaderData, char *mntpt) {
> -    moduleInfoSet modInfo = loaderData->modInfo;
> -    char file[200], dest[200];
> +    /* FIXME moduleInfoSet modInfo = loaderData->modInfo; */
> +    char file[200], dest[200], src[200];
>      char *title;
>      char *fwdir = NULL;
>      struct moduleBallLocation * location;
>      struct stat sb;
>      static int disknum = 0;
> -    int version = 1;
>      int fd, ret;
>  
> -    /* check for both versions */
> -    sprintf(file, "%s/rhdd", mntpt);
> +    /* check for new version */
> +    sprintf(file, "%s/rhdd3", mntpt);
>      if (access(file, R_OK)) {
> -        version = 0;
> -        sprintf(file, "%s/rhdd-6.1", mntpt);
> -        if (access(file, R_OK)) {
> -            /* this can't happen, we already verified it! */
> -            return LOADER_BACK;
> -        } 
> +      /* this can't happen, we already verified it! */
> +      return LOADER_BACK;
>      }
>      stat(file, &sb);
>      title = malloc(sb.st_size + 1);
> @@ -131,23 +272,35 @@ static int loadDriverDisk(struct loaderData_s *loaderData, char *mntpt) {
>      title[sb.st_size] = '\0';
>      close(fd);
>  
> -    sprintf(file, "/tmp/DD-%d", disknum);
> +    sprintf(file, DD_RPMS, disknum);

I'm really not a big fan of hiding the format specifier behind a macro, but
I do see the case for re-use here. Given that, it probably ought to be
defined somewhere very near to this code, not in a separate header file.

Also maybe name it something like DD_RPMDIR_TEMPLATE ?

>      mkdirChain(file);
> +    mkdirChain(DD_MODULES);
> +    mkdirChain(DD_FIRMWARE);
>  
>      if (!FL_CMDLINE(flags)) {
>          startNewt();
>          winStatus(40, 3, _("Loading"), _("Reading driver disk"));
>      }
>  
> -    sprintf(dest, "/tmp/DD-%d", disknum);
> -    copyDirectory(mntpt, dest, copyWarnFn, copyErrorFn);
> -
>      location = malloc(sizeof(struct moduleBallLocation));
>      location->title = strdup(title);
> -    location->version = version;
>  
> -    checked_asprintf(&location->path, "/tmp/DD-%d/modules.cgz", disknum);
> -    checked_asprintf(&fwdir, "/tmp/DD-%d/firmware", disknum);
> +    checked_asprintf(&location->path, DD_MODULES);
> +    checked_asprintf(&fwdir, DD_FIRMWARE);
> +
> +    sprintf(dest, DD_RPMS, disknum);
> +    sprintf(src, "%s/rpms/%s", mntpt, getProductArch());
> +    copyDirectory(src, dest, copyWarnFn, copyErrorFn);
> +
> +    /* unpack packages from dest into location->path */
> +    if(dlabelUnpackRPMDir(dest, DD_EXTRACTED)){
> +      /* TODO error handler */
> +    }

There's no way to recover, really - log and don't use this DD?

> +
> +    /* run depmod to refresh modules db */
> +    if(system("depmod -a")){
> +        /* FIXME: depmod didn't run */

Again, there's no graceful way out here - log it and abort using this driver disk?

> +    }
>  
>      if (!access(fwdir, R_OK|X_OK)) {
>          add_fw_search_dir(loaderData, fwdir);
> @@ -156,8 +309,11 @@ static int loadDriverDisk(struct loaderData_s *loaderData, char *mntpt) {
>      }
>      free(fwdir);
>  
> -    sprintf(file, "%s/modinfo", mntpt);
> -    readModuleInfo(file, modInfo, location, 1);
> +    /* TODO generate and read module info
> +     *
> +     * sprintf(file, "%s/modinfo", mntpt);
> +     * readModuleInfo(file, modInfo, location, 1);
> +     */
>  
>      if (!FL_CMDLINE(flags))
>          newtPopWindow();
> @@ -623,3 +779,106 @@ static void getDDFromDev(struct loaderData_s * loaderData, char * dev) {
>      umount("/tmp/drivers");
>      unlink("/tmp/drivers");
>  }
> +
> +/*
> + * Utility functions to maintain linked-list of device names
> + * */
> +
> +struct ddlist* ddlist_add(struct ddlist *list, const char* device)
> +{
> +  struct ddlist* item;
> +
> +  item = (struct ddlist*)malloc(sizeof(struct ddlist));
> +  if(item==NULL){
> +    return list;
> +  }
> +
> +  item->device = strdup(device);
> +  item->next = list;
> +
> +  return item;
> +}
> +
> +int ddlist_free(struct ddlist *list)
> +{
> +  struct ddlist *next;
> +  int count = 0;
> +
> +  while(list!=NULL){
> +    next = list->next;
> +    free(list->device);
> +    free(list);
> +    list = next;
> +    count++;
> +  }
> +
> +  return count;
> +}

Seems like using glib's linked lists is better than adding our own.

> +
> +
> +/*
> + * Look for partition with specific label (part of #316481)
> + */
> +struct ddlist* findDriverDiskByLabel(void)
> +{
> +    char *ddLabel = "OEMDRV";
> +    struct ddlist *ddDevice = NULL;
> +    blkid_cache bCache;
> +    
> +    int res;
> +    blkid_dev_iterate bIter;
> +    blkid_dev bDev;
> +
> +    if(blkid_get_cache(&bCache, NULL)<0){
> +      logMessage(ERROR, _("Cannot initialize cache instance for blkid"));
> +      return NULL;
> +    }
> +    if((res = blkid_probe_all(bCache))<0){
> +      logMessage(ERROR, _("Cannot probe devices in blkid: %d"), res);
> +      return NULL;
> +    }
> +
> +    bIter = blkid_dev_iterate_begin(bCache);
> +    blkid_dev_set_search(bIter, "LABEL", ddLabel);
> +    while((res = blkid_dev_next(bIter, &bDev))!=0){
> +        bDev = blkid_verify(bCache, bDev);
> +        if(!bDev)
> +          continue;
> +        logMessage(DEBUGLVL, _("Adding driver disc %s to the list of available DDs."), blkid_dev_devname(bDev));
> +        ddDevice = ddlist_add(ddDevice, blkid_dev_devname(bDev));
> +        /*blkid_free_dev(bDev); -- probably taken care of by the put cache call.. it is not exposed in the API */
> +    }
> +    blkid_dev_iterate_end(bIter);
> +    
> +    blkid_put_cache(bCache);
> +
> +    return ddDevice;
> +}
> +
> +int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device)
> +{
> +    int rc;
> +
> +    logMessage(INFO, "trying to mount %s", device);
> +    if (doPwMount(device, "/tmp/drivers", "auto", "ro", NULL)) {
> +	    logMessage(ERROR, _("Failed to mount driver disk."));
> +	    return -1;
> +    }
> +
> +    rc = verifyDriverDisk("/tmp/drivers");
> +    if (rc == LOADER_BACK) {
> +	logMessage(ERROR, _("Driver disk is invalid for this "
> +			 "release of %s."), getProductName());
> +	umount("/tmp/drivers");
> +	return -2;
> +    }
> +
> +    rc = loadDriverDisk(loaderData, "/tmp/drivers");
> +    umount("/tmp/drivers");
> +    if (rc == LOADER_BACK) {
> +	return -3;
> +    }
> +
> +    return 0;
> +}
> +
> diff --git a/loader/driverdisk.h b/loader/driverdisk.h
> index e6e919d..3e442fb 100644
> --- a/loader/driverdisk.h
> +++ b/loader/driverdisk.h
> @@ -24,6 +24,11 @@
>  #include "modules.h"
>  #include "moduleinfo.h"
>  
> +#define DD_RPMS "/tmp/DD-%d"
> +#define DD_EXTRACTED "/tmp/DD"
> +#define DD_MODULES "/tmp/DD/lib/modules"
> +#define DD_FIRMWARE "/tmp/DD/lib/firmware"
> +
>  extern char *ddFsTypes[];
>  
>  int loadDriverFromMedia(int class, struct loaderData_s *loaderData,
> @@ -39,4 +44,19 @@ void useKickstartDD(struct loaderData_s * loaderData, int argc,
>  
>  void getDDFromSource(struct loaderData_s * loaderData, char * src);
>  
> +int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device);
> +
> +struct ddlist {
> +  char* device;
> +  struct ddlist* next;
> +};
> +
> +struct ddlist* ddlist_add(struct ddlist *list, const char* device);
> +int ddlist_free(struct ddlist *list);
> +
> +struct ddlist* findDriverDiskByLabel(void);
> +
> +int modprobeNormalmode();
> +int modprobeDDmode();
> +
>  #endif
> diff --git a/loader/loader.c b/loader/loader.c
> index cd3e178..3159513 100644
> --- a/loader/loader.c
> +++ b/loader/loader.c
> @@ -959,6 +959,10 @@ static void parseCmdLineFlags(struct loaderData_s * loaderData,
>          else if (!strcasecmp(argv[i], "dd") || 
>                   !strcasecmp(argv[i], "driverdisk"))
>              flags |= LOADER_FLAGS_MODDISK;
> +        else if (!strcasecmp(argv[i], "dlabel=on"))
> +            flags |= LOADER_FLAGS_AUTOMODDISK;
> +        else if (!strcasecmp(argv[i], "dlabel=off"))
> +            flags &= ~LOADER_FLAGS_AUTOMODDISK;
>          else if (!strcasecmp(argv[i], "rescue"))
>              flags |= LOADER_FLAGS_RESCUE;
>          else if (!strcasecmp(argv[i], "nopass"))
> @@ -1785,6 +1789,7 @@ int main(int argc, char ** argv) {
>      struct loaderData_s loaderData;
>  
>      char *path;
> +    struct ddlist *dd, *dditer;
>  
>      gchar *cmdLine = NULL, *ksFile = NULL, *virtpcon = NULL;
>      gboolean testing = FALSE, mediacheck = FALSE;
> @@ -1882,6 +1887,12 @@ int main(int argc, char ** argv) {
>      flags |= LOADER_FLAGS_NOSHELL;
>  #endif
>  
> +    /* XXX if RHEL, enable the AUTODD feature by default,
> +     * but we should come with more general way how to control this */
> +    if(!strncmp(getProductName(), "Red Hat", 7)){
> +        flags |= LOADER_FLAGS_AUTOMODDISK;
> +    }
> +

You probably ought to split this checking out to its own function somewhere.

>      openLog(FL_TESTING(flags));
>      if (!FL_TESTING(flags))
>          openlog("loader", 0, LOG_LOCAL0);
> @@ -1946,6 +1957,26 @@ int main(int argc, char ** argv) {
>      /* FIXME: this is a bit of a hack */
>      loaderData.modInfo = modInfo;
>  
> +    /* Setup depmod & modprobe so we can load multiple DDs */
> +    modprobeDDmode();
> +
> +    if(FL_AUTOMODDISK(flags)){
> +        /* Load all autodetected DDs */
> +        logMessage(INFO, "Trying to detect vendor driver discs");
> +        dd = findDriverDiskByLabel();
> +        dditer = dd;
> +        while(dditer){
> +            if(loadDriverDiskFromPartition(&loaderData, dditer->device)){
> +                logMessage(ERROR, "Automatic driver disk loader failed for %s.", dditer->device);
> +            }
> +            else{
> +                logMessage(INFO, "Automatic driver disk loader succeeded for %s.", dditer->device);
> +            }
> +            dditer = dditer->next;
> +        }
> +        ddlist_free(dd);
> +    }
> +
>      if (FL_MODDISK(flags)) {
>          startNewt();
>          loadDriverDisks(DEVICE_ANY, &loaderData);
> @@ -1955,6 +1986,9 @@ int main(int argc, char ** argv) {
>          logMessage(INFO, "found /dd.img, loading drivers");
>          getDDFromSource(&loaderData, "path:/dd.img");
>      }
> +    
> +    /* Reset depmod & modprobe to normal mode and get the rest of drivers*/
> +    modprobeNormalmode();
>  
>      /* this allows us to do an early load of modules specified on the
>       * command line to allow automating the load order of modules so that
> @@ -2139,6 +2173,9 @@ int main(int argc, char ** argv) {
>          tmparg++;
>      }
>  
> +    if (FL_AUTOMODDISK(flags))
> +        *argptr++ = "--dlabel";
> +
>      if (FL_NOIPV4(flags))
>          *argptr++ = "--noipv4";
>  
> diff --git a/loader/loader.h b/loader/loader.h
> index ebf3766..ca6404f 100644
> --- a/loader/loader.h
> +++ b/loader/loader.h
> @@ -70,6 +70,7 @@
>  #define LOADER_FLAGS_HAVE_CMSCONF       (((uint64_t) 1) << 37)
>  #define LOADER_FLAGS_NOKILL		(((uint64_t) 1) << 38)
>  #define LOADER_FLAGS_KICKSTART_SEND_SERIAL   (((uint64_t) 1) << 39)
> +#define LOADER_FLAGS_AUTOMODDISK        (((uint64_t) 1) << 40)
>  
>  #define FL_TESTING(a)            ((a) & LOADER_FLAGS_TESTING)
>  #define FL_TEXT(a)               ((a) & LOADER_FLAGS_TEXT)
> @@ -107,6 +108,7 @@
>  #define FL_HAVE_CMSCONF(a)       ((a) & LOADER_FLAGS_HAVE_CMSCONF)
>  #define FL_NOKILL(a)		 ((a) & LOADER_FLAGS_NOKILL)
>  #define FL_KICKSTART_SEND_SERIAL(a) ((a) & LOADER_FLAGS_KICKSTART_SEND_SERIAL)
> +#define FL_AUTOMODDISK(a)        ((a) & LOADER_FLAGS_AUTOMODDISK)
>  
>  void startNewt(void);
>  void stopNewt(void);

The flags code in general looks pretty good.

-- 
        Peter

Growth for the sake of growth is the ideology of the cancer cell.

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list

[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux