Blame src/module.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2015 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include "trio.h"
Packit 8f70b4
#include <string.h>
Packit 8f70b4
#ifdef HAVE_DLFCN_H
Packit 8f70b4
# include <dlfcn.h>
Packit 8f70b4
#endif
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <stddef.h>
Packit 8f70b4
#include "module.h"
Packit 8f70b4
#include "ResMgr.h"
Packit 8f70b4
#include "configmake.h"
Packit 8f70b4
#include "xstring.h"
Packit 8f70b4
Packit 8f70b4
#ifndef RTLD_GLOBAL
Packit 8f70b4
# define RTLD_GLOBAL 0
Packit 8f70b4
#endif
Packit 8f70b4
#ifdef RTLD_NOW
Packit 8f70b4
# define DLOPEN_FLAGS RTLD_NOW|RTLD_GLOBAL
Packit 8f70b4
#else
Packit 8f70b4
/* SunOS4 manual says it is reserved and must be 1 */
Packit 8f70b4
# define DLOPEN_FLAGS 1
Packit 8f70b4
#endif
Packit 8f70b4
#ifdef RTLD_LAZY
Packit 8f70b4
# define DLOPEN_FLAGS_LAZY RTLD_LAZY|RTLD_GLOBAL
Packit 8f70b4
#else
Packit 8f70b4
# define DLOPEN_FLAGS_LAZY DLOPEN_FLAGS
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
typedef void (*init_t)(int,const char*const*);
Packit 8f70b4
Packit 8f70b4
/* TODO: this can go to a config file. */
Packit 8f70b4
static const char * const module_aliases[]=
Packit 8f70b4
{
Packit 8f70b4
   "proto-hftp",  "proto-http",
Packit 8f70b4
#if USE_SSL
Packit 8f70b4
   "proto-https", "proto-http",
Packit 8f70b4
   "proto-ftps",  "proto-ftp",
Packit 8f70b4
#endif
Packit 8f70b4
   "cmd-at",	  "cmd-sleep",
Packit 8f70b4
   "cmd-repeat",  "cmd-sleep",
Packit 8f70b4
   NULL
Packit 8f70b4
};
Packit 8f70b4
Packit 8f70b4
class lftp_module_info
Packit 8f70b4
{
Packit 8f70b4
   lftp_module_info *next;
Packit 8f70b4
   static lftp_module_info *base;
Packit 8f70b4
Packit 8f70b4
   char *path;
Packit 8f70b4
   void *addr;
Packit 8f70b4
Packit 8f70b4
public:
Packit 8f70b4
   lftp_module_info(const char *p,void *a)
Packit 8f70b4
      {
Packit 8f70b4
	 path=xstrdup(p);
Packit 8f70b4
	 addr=a;
Packit 8f70b4
	 next=base;
Packit 8f70b4
	 base=this;
Packit 8f70b4
      }
Packit 8f70b4
   ~lftp_module_info()
Packit 8f70b4
      {
Packit 8f70b4
	 xfree(path);
Packit 8f70b4
	 for(lftp_module_info **scan=&bas;; *scan; scan=&scan[0]->next)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(*scan==this)
Packit 8f70b4
	    {
Packit 8f70b4
	       *scan=scan[0]->next;
Packit 8f70b4
	       break;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   static lftp_module_info *find_module(const char *name)
Packit 8f70b4
      {
Packit 8f70b4
	 int name_len=strlen(name);
Packit 8f70b4
	 for(lftp_module_info *scan=base; scan; scan=scan->next)
Packit 8f70b4
	 {
Packit 8f70b4
	    char *slash=strrchr(scan->path,'/');
Packit 8f70b4
	    char *scan_name=(slash?slash+1:scan->path);
Packit 8f70b4
	    if(!strncmp(scan_name,name,name_len)
Packit 8f70b4
	    && (scan_name[name_len]==0 || !strcmp(&scan_name[name_len],".so")))
Packit 8f70b4
	    {
Packit 8f70b4
	       return scan;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
   static void delete_by_name(const char *name)
Packit 8f70b4
      {
Packit 8f70b4
	 lftp_module_info *m=find_module(name);
Packit 8f70b4
	 if(m)
Packit 8f70b4
	    delete m;
Packit 8f70b4
      }
Packit 8f70b4
};
Packit 8f70b4
lftp_module_info *lftp_module_info::base;
Packit 8f70b4
Packit 8f70b4
static ResDecl res_mod_path("module:path", PKGLIBDIR "/" VERSION ":" PKGLIBDIR, 0,0);
Packit 8f70b4
Packit 8f70b4
/* dlopen can take a file without extension and automatically do the
Packit 8f70b4
 * right thing, however that doesn't fit with this code that tries to
Packit 8f70b4
 * stat before the dlopen call, hence need some help here */
Packit 8f70b4
#if defined(__MACH__) && defined(__APPLE__)
Packit 8f70b4
static const char ext[] = ".bundle";
Packit 8f70b4
#else
Packit 8f70b4
static const char ext[] = ".so";
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
static int access_so(xstring &fullpath)
Packit 8f70b4
{
Packit 8f70b4
   int res=access(fullpath,F_OK);
Packit 8f70b4
   if(res==-1)
Packit 8f70b4
   {
Packit 8f70b4
      if(!fullpath.ends_with(ext))
Packit 8f70b4
	 fullpath.append(ext);
Packit 8f70b4
      res=access(fullpath,F_OK);
Packit 8f70b4
   }
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static const char *find_module_alias(const char *path)
Packit 8f70b4
{
Packit 8f70b4
   const char *const *scan;
Packit 8f70b4
   for(scan=module_aliases; *scan; scan+=2)
Packit 8f70b4
      if(!strcmp(path,*scan))
Packit 8f70b4
	 return scan[1];
Packit 8f70b4
   return path;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void *module_load(const char *path,int argc,const char *const *argv)
Packit 8f70b4
{
Packit 8f70b4
#ifdef HAVE_DLOPEN
Packit 8f70b4
   void *map;
Packit 8f70b4
   const char *modules_path=res_mod_path.Query(path);
Packit 8f70b4
   xstring fullpath;
Packit 8f70b4
   init_t init;
Packit 8f70b4
Packit 8f70b4
   if(strchr(path,'/'))
Packit 8f70b4
   {
Packit 8f70b4
      fullpath.set(path);
Packit 8f70b4
      access_so(fullpath);
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      path=find_module_alias(path);
Packit 8f70b4
      char *p=alloca_strdup(modules_path);
Packit 8f70b4
      for(p=strtok(p,":"); p; p=strtok(0,":"))
Packit 8f70b4
      {
Packit 8f70b4
	 fullpath.vset(p,"/",path,NULL);
Packit 8f70b4
	 if(access_so(fullpath)==0)
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
      if(p==0)
Packit 8f70b4
      {
Packit 8f70b4
	 fullpath.vset(PKGLIBDIR,"/",VERSION,"/",path,NULL); // fallback
Packit 8f70b4
	 access_so(fullpath);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   map=dlopen(fullpath,DLOPEN_FLAGS);  // LAZY?
Packit 8f70b4
   if(map==0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   (void)new lftp_module_info(fullpath,map);
Packit 8f70b4
#if 0 // for some reason this does not work even with LAZY (because of _init?).
Packit 8f70b4
   const char*const*depend=(const char*const*)dlsym(map,"module_depend");
Packit 8f70b4
   if(depend)
Packit 8f70b4
   {
Packit 8f70b4
      while(*depend)
Packit 8f70b4
      {
Packit 8f70b4
	 if(lftp_module_info::find_module(*depend)==0)
Packit 8f70b4
	 {
Packit 8f70b4
	    void *dep=module_load(*depend,0,0);
Packit 8f70b4
	    if(!dep)
Packit 8f70b4
	       fprintf(stderr,_("depend module `%s': %s\n"),*depend,module_error_message());
Packit 8f70b4
	 }
Packit 8f70b4
	 depend++;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
#endif //0
Packit 8f70b4
   init=(init_t)dlsym(map,"module_init");
Packit 8f70b4
   if(init)
Packit 8f70b4
      (*init)(argc,argv);
Packit 8f70b4
   return map;
Packit 8f70b4
#else // !HAVE_DLOPEN
Packit 8f70b4
   return 0;
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *module_error_message()
Packit 8f70b4
{
Packit 8f70b4
#ifdef HAVE_DLOPEN
Packit 8f70b4
   return dlerror();
Packit 8f70b4
#else
Packit 8f70b4
   return _("modules are not supported on this system");
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool module_init_preloaded(const char *module)
Packit 8f70b4
{
Packit 8f70b4
#if defined(HAVE_DLOPEN) && defined(RTLD_DEFAULT)
Packit 8f70b4
   module=find_module_alias(module);
Packit 8f70b4
   char *init_fn=alloca_strdup2(module,12);
Packit 8f70b4
   // change dashes to underscores in the function name
Packit 8f70b4
   for(char *scan=init_fn; *scan; scan++)
Packit 8f70b4
      if(*scan=='-')
Packit 8f70b4
	 *scan='_';
Packit 8f70b4
   strcat(init_fn,"_module_init");
Packit 8f70b4
   init_t init=(init_t)dlsym(RTLD_DEFAULT,init_fn);
Packit 8f70b4
   if(init) {
Packit 8f70b4
      (*init)(0,0);
Packit 8f70b4
      return true;
Packit 8f70b4
   }
Packit 8f70b4
#endif
Packit 8f70b4
   return false;
Packit 8f70b4
}