ose_ddll_drv.c

来自「OTP是开放电信平台的简称」· C语言 代码 · 共 379 行

C
379
字号
/* ``The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved via the world wide web at http://www.erlang.org/. *  * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. *  * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' *  *     $Id$ *//*  * Interface functions to the dynamic linker using dl* functions. */#ifdef HAVE_CONFIG_H#  include "config.h"#endif#include "sys.h"#include "global.h"#include "erl_ddll.h"#include "ose.h"#include "mms_err.h"#include "errno.h"#include "sys/stat.h"#include "dbgprintf.h"#include "efs.h"#include "stdlib.h"#include "stdio.h"#include "string.h"#include "prh.sig"#include "prhfuncs.h"#define NO_DRIVER_MACROS#include "ose_erl_driver.h"#undef  NO_DRIVER_MACROS#define MAX_NAME_LEN 255       /* XXX should we get the system path size? */#define ATTACH_SIGNO (~0UL)#define PLS_SERVER "ose_pls_elf/"#define ERL_NO_PLS       -1#define ERL_DRV_NOT_REG  -2#define ERL_DRV_EXISTS   -3#define ERL_NO_HND       -4static int dlopen_error;	/* set if error occurs */extern int get_pgm_data(char*, void**, void*, int*);extern int add_pgm_handle(char*, PrhProgramHandle);extern void del_pgm(char*);union SIGNAL{  SIGSELECT sigNo;  struct PrhLoadProgramRequest loadProgramRequest;  struct PrhLoadProgramReply loadProgramReply;  struct PrhStartProgramRequest startProgramRequest;  struct PrhStartProgramReply startProgramReply;  struct PrhKillProgramRequest killProgramRequest;  struct PrhKillProgramReply killProgramReply;  struct PrhRemoveLMRequest removeLMRequest;  struct PrhRemoveLMReply removeLMReply;};static ErlDrvIf erl_drv_funcs = {  driver_select,  driver_event,  driver_output,  driver_output2,  driver_output_binary,  driver_outputv,  driver_vec_to_buf,  driver_set_timer,  driver_cancel_timer,  driver_read_timer,  erl_errno_id,  driver_failure_eof,  driver_failure_atom,  driver_failure_posix,  driver_failure,  driver_exit,  set_busy_port,  set_port_control_flags,  get_port_flags,  driver_alloc_binary,  driver_realloc_binary,  driver_free_binary,  driver_alloc,  driver_realloc,  driver_free,  driver_enq,  driver_pushq,  driver_deq,  driver_sizeq,  driver_enq_bin,  driver_pushq_bin,  driver_peekqv,  driver_peekq,  driver_enqv,  driver_pushqv,  add_driver_entry,  remove_driver_entry,  driver_mk_atom,  driver_mk_port,  driver_connected,  driver_caller,  driver_output_term,  driver_send_term,  driver_async,  driver_async_cancel,  driver_lock_driver};static int pls_req(union SIGNAL **sig) {   PROCESS pls_;  OSATTREF attref;  static const SIGSELECT select[] = {5, PRH_LOAD_PROGRAM_REPLY,  				        PRH_START_PROGRAM_REPLY, 				        PRH_KILL_PROGRAM_REPLY, 				        PRH_REMOVE_LM_REPLY, 				        ATTACH_SIGNO};  if(!hunt(PLS_SERVER, 0, &pls_, NULL)) {    free_buf(sig);    dlopen_error = ERL_NO_PLS;    return -1;  }   send(sig, pls_);  *sig = alloc(sizeof(SIGSELECT), ATTACH_SIGNO);  attref = attach(sig, pls_);  *sig = receive((SIGSELECT *) select);  if((*sig)->sigNo == ATTACH_SIGNO) {    free_buf(sig);    dlopen_error = ERL_NO_PLS;    return -1;  }  detach(&attref);  return 0;}/* * ddll_open(name) * * name is the identity of the load module containing a driver  * (one driver per LM since ddll_drv will only add one driver entry * per loading occasion). The identity of the LM must be the name of  * the driver. An elf extension should never be specified. If an LM  * "name" isn't found, "name.elf" will be searched for automatically. * * It is assumed that an AS has already been connected! * * Note: erl_ddll:load_driver/2 is also called for a static driver  * linked with erts (so that the driver entry may be added to  * driver_list, see io.c). For this reason we need to check here if  * the driver is registered as static and, if so, ignore loading a * module. * * Returns a pointer to the init function of the driver. */void *ddll_open(char *fullname){  struct PrhConnectAsfReply *conReply;  struct PrhLoadProgramReply *loadReply;  PrhProgramHandle programHandle;  PrhStatus res;  union SIGNAL *sig;  int size, is_static;  void *entrypoint;  char *name;  char *elfname;  dlopen_error = 0;  /* name does not contain a path, ignore the initial '/' */  name = fullname + 1;  /* If the driver is already registered, we'll skip the loading     procedure and return the registered entrypoint (the user has to     unload a dynaimc driver before loading a new one with the same      name). This is necessary for static drivers, but also to avoid     data inconsistency if a driver has been previously loaded and      started from outside erlang. */  if(get_pgm_data(name, &entrypoint, NULL, &is_static)) {    DEBUGF(("static or already registered with %X\n", entrypoint));    return entrypoint;  } /* load module */    size = strlen(name) + 2;  sig = alloc(sizeof(struct PrhLoadProgramRequest) + size,	      PRH_LOAD_PROGRAM_REQUEST);  sig->loadProgramRequest.programHandle = PRH_ALLOCATE_HANDLE;  sig->loadProgramRequest.options = 0;  prh_init_strings(&sig->loadProgramRequest.strings);  prh_add_string(&sig->loadProgramRequest.strings, name);  DEBUGF(("Loading %s...\n", name));  if(pls_req(&sig) < 0)    return NULL;  loadReply = &sig->loadProgramReply;  /* if LM wasn't found, try with an elf extension */  if(loadReply->status == PRH_ELM_NOT_FOUND) {    free_buf(&sig);    elfname = driver_alloc(strlen(name) + 5);    sprintf(elfname, "%s.elf", name);    size = strlen(elfname) + 2;    sig = alloc(sizeof(struct PrhLoadProgramRequest) + size,		PRH_LOAD_PROGRAM_REQUEST);    sig->loadProgramRequest.programHandle = PRH_ALLOCATE_HANDLE;    sig->loadProgramRequest.options = 0;    prh_init_strings(&sig->loadProgramRequest.strings);    prh_add_string(&sig->loadProgramRequest.strings, elfname);    DEBUGF(("Loading %s...\n", elfname));    driver_free(elfname);    if(pls_req(&sig) < 0)      return NULL;  }  /* start program (which should register the driver) */  if(loadReply->status == PRH_SUCCESS) {    programHandle = loadReply->programHandle;    free_buf(&sig);       sig = alloc(sizeof(struct PrhStartProgramRequest),		PRH_START_PROGRAM_REQUEST);    sig->startProgramRequest.programHandle = programHandle;    DEBUGF(("Starting %s(%u)...\n", name, programHandle));    if(pls_req(&sig) < 0)      return NULL;    if(sig->startProgramReply.status == PRH_SUCCESS) {      free_buf(&sig);      DEBUGF(("Mapping %s -> %u...\n", name, programHandle));      /* save program handle with name and entrypoint */      if(!add_pgm_handle(name, programHandle)) {	dlopen_error = ERL_DRV_NOT_REG;	return NULL;      }      get_pgm_data(name, &entrypoint, NULL, NULL);      return entrypoint;    }    dlopen_error = sig->startProgramReply.status;    free_buf(&sig);    return NULL;  }  dlopen_error = loadReply->status;  free_buf(&sig);  return NULL;}/* We have "fooled" ddll_drv to call this function instead of the true driver   init function. Here we know the address of the true driver init function    and can call it with a struct containing pointers to the functions in the    erlang driver interface. */static ErlDrvEntry* ddll_init_caller(DE_Handle* dh){  ErlDrvEntry* (*init_func)(ErlDrvIf*);  init_func = dh->handle;  return (*init_func)(&erl_drv_funcs);}/* * ddll_sym is normally used to find the address of an arbitrary symbol. Here,  * instead, we return the address of a function implemented in this file. This  * function will use the handle element in the DE_Handle struct (which it  * receives from ddll_drv) to retrieve the true driver init function address * and then call this init function with a struct containing pointers to the * functions in the erlang driver interface. *  * The init function of the driver should have been registered during load * module program start. (It's important that PLS does not send  * startProgramReply until the driver has finished registering itself with  * the erts pgm server process, i.e. the driver should register during the  * LM initialisation phase).  */void *ddll_sym(void *handle, char *func_name){  return &ddll_init_caller;}/*  * Close a driver, meaning stop the program and unregister the driver. */int ddll_close(void *entrypoint){   union SIGNAL *sig;   static const SIGSELECT select[] = {3, PRH_KILL_PROGRAM_REPLY, 				         PRH_REMOVE_LM_REPLY, 				         ATTACH_SIGNO};   PrhStatus status;   char name[256];   void *tmp;   void *hnd;   int is_static;   /* lookup the LM handle */   name[0] = '\0';   if(get_pgm_data(name, &entrypoint, &hnd, &is_static)) {     if(is_static) {       /* don't unregister a static driver, ignore */       return 0;     }      else if(hnd == NULL) {	/* not loaded by erlang */       del_pgm(name);       dlopen_error = ERL_NO_HND;       return -1;     }   } else {			/* not found */     dlopen_error = ERL_DRV_NOT_REG;     return -1;   }   DEBUGF(("Removing entry %s at %X (handle %u)\n", name, entrypoint, hnd));   /** at this point we know it's a registered dynamic driver **/   /* kill the program */   sig = alloc(sizeof (struct PrhKillProgramRequest),	       PRH_KILL_PROGRAM_REQUEST);   sig->killProgramRequest.programHandle = (PrhProgramHandle)hnd;   if(pls_req(&sig) < 0)     return -1;   if(sig->killProgramReply.status != PRH_SUCCESS) {     dlopen_error = sig->killProgramReply.status;     free_buf(&sig);     return -1;   }   free_buf(&sig);   /* delete the program data */   del_pgm(name);   if(get_pgm_data(NULL, &entrypoint, &hnd, &is_static)) {     fprintf(stderr, "Entry %X not deleted!\n", entrypoint);     return -1;   }     return 0;}/* * Return string that describes the (current) error */char *ddll_error() {  static char msg[128];  if(dlopen_error != 0) {    switch(dlopen_error) {    case ERL_NO_PLS:      strcpy(msg, "cannot contact pls server");      break;    case ERL_DRV_NOT_REG:      strcpy(msg, "driver has not been properly registered");      break;    case ERL_DRV_EXISTS:      strcpy(msg, "driver is already loaded");      break;    case ERL_NO_HND:      strcpy(msg, "driver wasn't loaded with load_driver/2, can't unload");      break;    default:      sprintf(msg, "prh load/unload or start/kill error, status = %d", dlopen_error);     }    return msg;  }  return "unknown error";}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?