InterBase Installation API - Design Specification
High-Level Design
As mentioned in the Functional Specification the Install API will, at first, only be implemented for Windows 95 and Windows NT 4.0. Furthermore, we know that the way the InterBase products are installed on windows will always be different than the way they are installed on the UNIX platforms. Even though the overall design of this API should not restrict its implementation, we understand that the implementation will be different for Windows than it will be on the UNIX platforms. For these reasons we will be able to take advantage of the Microsoft Setup API for the first implementation of our Install API.
High Level Algorithm
The Microsoft Setup API provides functionality for creating sets of files to be copied, along with the conditions upon which to perform the copy operation. This includes whether to over-write an existing file or to perform version checking. In addition the Microsoft Setup API provides logging functionality to record all actions taken by the Setup API. Even though the Microsoft Setup API is quite powerful, we will still need to write all the logic needed to install the products in such a way that we can install the InterBase product.
We will also need to write an abstraction layer to isolate the generic install logic from the platform specific installation code, such as copying files and checking for registry keys. This is done to facilitate the future implementation of the Install API on other platforms. This means that the functions that determine which InterBase product we are installing, and which options are valid will in turn call platform specific functions to actually perform the precheck and the installation.
The high level algorithm for the eight API functions is as follows:
isc_install_set_option (phandle, option)
The first time this function is called handle should be set to null by the caller. This indicates that the options list is not allocated. Each time this function is called, it will allocate memory for a new option node, set the values of this node to contain the value of option, and add it to the linked list of options. The head of this linked list is pointed to by handle, and is maintained by the install engine. Each function will in turn be passed the address of handle which will be used to determine the options requested.
Note
This design prepares us for a multi-threaded install engine, although no effort will be spent on implementing this at this time. Furthermore, the user can call this function multiple times with handle set to 0L to allocate multiple options list.
isc_install_unset_option (phandle, option)
This function will traverse the options list to find and remove the option specified by option. If option is the only option in the list, will be set to 0L to indicate an empty options list. If handle is set to 0L when this function is called, a warning will be generated to indicate that the options list is empty.
isc_install_clear_options (phandle)
This function will clear all the options stored in handle. This needs to be called anytime the options need to be rebuilt, and may be called at the end of the install process. Upon successful completion handle is set to 0L. If handle is already 0L then a warning will be returned since the handle has not been allocated.
isc_install_precheck (handle, source_path, dest_path)
This function will perform all the pre install checks needed. These are:
- Check that we are running on a valid operating system. Currently these are Windows 95, Windows 98, and Windows NT 4.0, if these are not detected, return an error.
- Check that the source_path and dest_path are valid, if not return an error. This check is skipped for source_path if it is NULL or an empty string, otherwise it is checked that the correct permissions exist for reading from source_path and it is a directory . dest_path is only checked when it is specified by the user and it is not NULL. dest_path is checked that it is not on a network drive and that it is a writeable directory. It is also checked for the availability of the necessary disk space for the options requested on dest_path and for the files which must go into the windows system directory. If any of the checks fail the error is immediately returned. Not all of the checks are performed when the directory does not actually exists.
- Check for dependencies between options requested. The dependencies between the IB_ options are as follows:
- The IB_CMD_TOOLS, IB_GUI_TOOLS, IB_DEV, and IB_ODBC options need the IB_CLIENT option to function correctly. This also applies to all individual IB_CMD_TOOLS_ and IB_GUI_TOOLS_ options. Warning is returned.
- The IB_EXAMPLES option needs the IB_CLIENT and the IB_DEV options to function correctly. Warning is returned.
- Check to see if a Classic server is present, if so return an error.
- Check for a Super server already running. If one is already running, return an error. This check is skipped if the IB_SERVER option is not specified.
- Check for correct user permissions needed to install the server service to the Windows Services Manages on NT, and for the addition of registry entries. If the user running the install does not have Administrator privileges, then return an error. This check is skipped if the IB_SERVER option is not specified.
Some of the dependencies may return errors others may return warnings. It is up to the user to install when a warning is returned. The install will not proceed if isc_install_precheck returns an error.
isc_install_execute(handle, source_path, dest_path, fp_status, status_arg, fp_error, error_arg, uninstall_file_name)
This function will perform the actual install, that is:
- Call isc_install_precheck to ensure that the install may be performed. If isc_install_precheck returns an error the install is aborted.
- Log all actions to a file called ib_install.log in a temporary location.
- Create the destination directory if it does not already exist.
- Copy the files using all the correct version checks, and delayed copying methods necessary.
- Increase the reference count of shared files.
- Create the registry entries needed.
- Install the guardian in the Services Manager on Win NT, or add the guardian to the Run section of the registry on Win 95.
- Modify the TCP/IP services file if it exists.
- Stream options into ib_uninst.xxx (where xxx is a sequence number) for use at uninstall time.
- Free the options list from memory.
- Move ib_install.log to the install directory.
- Call fp_status at regular intervals to keep caller appraised of status.
- If at any point in the install is canceled (by the user, or by an error) attempt to cleanup.
isc_uninstall_precheck(uninstall_file_name)
This function will perform all pre uninstall checking needed, such as:
- Check that we run on supported OS version.
- Check that the uninstall file (ib_uninst.xxx) contains the streamed list of options.
- Check that the server is not running if the server was installed. Otherwise this check is skipped
- Check for Administrator permissions which are needed to uninstall Server (NT only).
isc_uninstall_execute(uninstall_file_name, fp_status, status_arg, fp_error, error_arg)
This function will perform the actual uninstall, that is:
- Call isc_uninstall_precheck to ensure that the uninstall may be performed.
- Decrease reference counts of shared files, and remove any which have a reference count less than 1, except for those file which Microsoft has pre assigned to 0, such as MSVCRT.DLL.
- Remove all InterBase files which are in the ib_uninst.xxx file, except for isc4.gdb, isc4.gbk, ib_license.dat
- Remove registry entries which are in ib_uninst.xxx.
- Uninstall the Guardian and Server service with the Services Manage on NT. Remove Run registry entries on 95-98.
- Call fp_status at regular intervals to keep caller appraised of status.
- If at any point in the uninstall is canceled by the user, or by an error attempt to cleanup.
isc_install_get_message (handle, msg_no, msg, msg_len)
This function converts the error/warning value stored in msg_no and returns the corresponding error/warning message for use by the programmer. Currently there is no OS specific error message returned.
isc_install_load_exetrnal_text (external_path)
The function is designed to load an .msg file with customer defined textual information . The file is to be created by a customer using our MAKEMSG utility.
MSG_NO isc_install_get_info( info_type, option, info_buf, buf_size);
The function will return the information requested by info_type. If the info_type is not option specific then option is ignored. The information is returned at info_buf location. This parameter can not be NULL. A proper buf_size in bytes should be specified. If either info_buf or buf_size are NULL(0) an error will be generated. Currently 4 types of the information can be expected from the function but it is possible to add additional ones as necessary. The types of information are:
- Suggested destination directory. This functionality is taken over from isc_install_precheck. isc_install_precheck will no longer suggest a destination path (Option parameter is ignored in this case).
- Disk space required to install a particular option (Requires a valid option)
- A human readable option name (Requires a valid option)
- A human readable option description. (Requires a valid option)
Data Structures/Class Hierarchy
This feature does not have any external data structures, however it does have some typedef's for the convenience of the user. These are:
typedef void* | OPTIONS_HANDLE This will essentially contain a pointer to a internal data structure with all necessary context information. |
typedef long | MSG_NO |
typedef unsigned long | OPT |
typedef char | TEXT |
typedef int (*fp_error)() | FP_ERROR |
typedef int (*fp_status)() | FP_STATUS |
typedef long | STRING_ID is essentially a synonym type for MSG_NO. The STRING_ID will be used to uniquely identify the textual string and to retrieve it using TXT_get_text. We need to define a new way to store text messages that will differ from the way we store them in the version 5.5 of the Install API. This is because we now might have not only the default text strings but also customized text strings defined by the customer. To accomplish a reliable processing of the customer defined text strings we need to carry out various checks when creating messages from a text file created by the customer. We also want to retain a flexible way of adding textual messages should we add a new installable option with corresponding textual name and description or error/warning strings. |
typedef char | TEXTSTR[ISC_INSTALL_MAX_MESSAGE_LEN] |
typedef TEXTSTR* | PTEXTSTR |
Interfaces
The user interface for this feature is already listed in the functional spec., since this feature is an API. The only other interface is the list of defines for the options, and the error message numbers. The list of options is a follows:
INTERBASE | 1000 |
IB_SERVER | 1001 |
IB_CLIENT | 1002 |
IB_CMD_TOOLS | 1003 |
IB_CMD_TOOLS_DB_MGMT | 1004 |
IB_CMD_TOOLS_USR_MGMT | 1005 |
IB_CMD_TOOLS_DB_QUERY | 1006 |
IB_GUI_TOOLS | 1007 |
IB_GUI_TOOLS_DB_QUERY | 1008 |
IB_GUI_TOOLS_USR_MGMT | 1009 |
IB_GUI_TOOLS_DB_MGMT | 1010 |
IB_DOC | 1011 |
IB_EXAMPLES | 1012 |
IB_DEV | 1013 |
IB_ODBC | 1014 |
isc_isntall_get_info interfaces are defined as follows
isc_install_info_destination | Request the function to suggest the destination directory |
isc_install_info_opname | Requests the function to return the human readable name of the option |
isc_install_info_opdesc | Requests the fucntion to return the human readable description of the option |
isc_install_info_opspace | Resquests the fucntion to return the amount of the disk space required for installation of the option specified. |
Detailed Design
Internal Data Structures
There is a need to define some structures to allow us to specify what each option value means, and what actions are to be taken in order to install that option. These structures are:
A member is any one of the options above. These can be groups of groups, groups of elements, or elements.
typedef unsigned long member
A dependency is a grouping of install and uninstall check functions which should be called before performing that action, they are represented by a depend structure.
typedef struct depend {
FP_CHECK fp_install_check;
int install_check_param1;
FP_CHECK fp_uninstallcheck;
int uninstall_check_param1;
} *DEPEND;
A component is grouping of options which can be individually selected. That is a group of groups, a group of elements, or an individual element.
typedef struct component {
member component_name; /* This is actually the option value */
member *component_members;
struct depend *component_dependencies;
unsigned long size;
TEXT* comp_disp_name;
TEXT* comp_description;
ACTION comp_actions; /* Array of actions */
STRING_ID perform_text; /* This will be used as a textual description of the action during install */
STRING_ID unperform_text; /* This will be used as a textual description of the action during uninstall */
} COMPONENT;
An action is what is to be performed when an individual element is selected. This can include copying files, or creating registry entries.
#define ISC_INSTALL_NUM_ACT_PARAM 3
typedef struct action {
FP_ACTION fp_perform;
FP_ACTION fp_unperform;
TEXT* action_param1;
TEXT* action_param2;
TEXT* action_param3;
STRING_ID perform_text; /* This will be used as a textual description of the action */
STRING_ID unperform_text;
} *ACTION;
An action_item is a node of a linked list of actions used to hold the list of actions to be performed once the list of options is parsed.
typedef struct action_item {
ACTION act;
BOOL ifperformed;
struct action_item* act_next;
struct action_item* act_prev;
} *ACTION_ITEM;
Once we combine these structures with some convenient macros, such as:
/* GROUP definitions */
#define DEFINE_GROUP(group_name) static OPT group_name##_MEMBERS[]={
#define MEMBER(member_name) member_name,
#define END_DEFINE_GROUP 0 };
/* Dependencies definitions */
#define DEFINE_DEPENDENCY(depend_name) static struct depend depend_name[]={
#define CHECK_ELEMENT(element) { UTIL_find_option, element, NULL, 0 },
#define CHECK_PRIV { PSC_check_permissions, 0, PSC_check_permissions, 0 },
#define CHECK_NOT_CLASSIC { PSC_classic_not_found, 0 , NULL, 0 },
#define CHECK_SERVER_NOT_RUNNING { PSC_server_running, 0, PSC_server_running, 0 },
#define END_DEFINE_DEPENDENCY { NULL, 0, NULL, 0 } };
/* Components definitions */
#define COMPONENTS static COMPONENT components[]={
#define GROUP(group_name, name, desc) { group_name, group_name##_MEMBERS, NULL, 0L, name, desc, NULL },
#define ELEMENT(el_name, depends, size, name, desc) { el_name, NULL, depends, size, name, desc, el_name##_ACTIONS },
#define END_COMPONENTS { 0 , NULL, NULL, 0L, 0L, 0L, NULL } };
/* Actions definitions */
#define ACTIONS(actionset_name)
static struct action actionset_name##_ACTIONS[]={
#define FILE_COPY(file_name){
(FP_ACTION)PSC_file_copy, (FP_ACTION) PSC_file_delete, file_name, NULL, NULL, txt_actions_copy_file, txt_actions_delete_file },
#define FILE_COPY_DIR(dir_name){
FP_ACTION)PSC_dir_copy, (FP_ACTION)PSC_dir_delete, dir_name, NULL, NULL, txt_actions_copy_dir, txt_actions_delete_dir },
#define FILE_COPY_NO_OVERWRITE(file_name){
(FP_ACTION)PSC_file_copy_no_overwrite, FP_ACTION)NULL, file_name, NULL, NULL , txt_actions_copy_no_over, txt_actions_skip_file },
#define FILE_COPY_SYS_NO_OVERWRITE(file_name) {
(FP_ACTION)PSC_file_copy_sys_no_overwrite, (FP_ACTION)NULL, file_name, NULL, NULL, txt_actions_copy_no_over, txt_actions_skip_file },
#define FILE_COPY_VER_CHECK(file_name){
(FP_ACTION)PSC_file_copy_ver_check, FP_ACTION)PSC_file_delete, file_name, NULL, NULL, txt_actions_copy_ver_check, txt_actions_delete_file },
#define FILE_COPY_SYSTEM(file_name){
(FP_ACTION)PSC_file_copy_system, (FP_ACTION)PSC_file_delete_system,(FP_ACTION)file_name, NULL, NULL, txt_actions_copy_system, txt_actions_delete_system },
#define FILE_COPY_WINDOWS(file_name){
(FP_ACTION)PSC_file_copy_windows, (FP_ACTION)PSC_file_delete_windows, file_name, NULL, NULL, txt_actions_copy_windows, txt_actions_delete_windows },
#define FILE_COPY_SHARE(file_name){
(FP_ACTION)PSC_copy_share,(FP_ACTION)PSC_delete_share, file_name, "Y", NULL, txt_actions_copy_share, txt_actions_delete_share },
#define FILE_COPY_SHARE_NO_DEL(file_name){
(FP_ACTION)PSC_copy_share,(FP_ACTION)PSC_delete_share,
file_name, "N", NULL, txt_actions_copy_share, txt_actions_skip_file },
#define FILE_COPY_SHARE_SYSTEM(file_name){
(FP_ACTION)PSC_copy_share_system,(FP_ACTION)PSC_delete_share_system, file_name, "Y", NULL, txt_actions_copy_share_system, txt_actions_delete_share_system },
#define FILE_COPY_SHARE_SYSTEM_NO_DEL(file_name){
(FP_ACTION)PSC_copy_share_system,(FP_ACTION)PSC_delete_share_system, file_name, "N", NULL, txt_actions_copy_share_system, txt_actions_skip_file },
#define FILE_COPY_SHARE_WINDOWS(file_name) {
(FP_ACTION)PSC_copy_share_windows,(FP_ACTION)PSC_delete_share_windows, file_name, "Y", NULL, txt_actions_copy_share_windows, txt_actions_delete_share_windows },
#define FILE_COPY_SHARE_WINDOWS_NO_DEL(file_name){
(FP_ACTION)PSC_copy_share_windows,(FP_ACTION)PSC_delete_share_windows, file_name, "N", NULL, txt_actions_copy_share_windows, txt_actions_skip_file },
#define REGISTRY_ADD_KEY(root,path, keyclass){
(FP_ACTION)PSC_registry_add_key,(FP_ACTION)PSC_registry_delete_key, root, path, keyclass, txt_actions_create_reg_key, txt_actions_delete_reg_key },
#define REGISTRY_ADD_VALUE(root,path,value){
(FP_ACTION)PSC_registry_add_value,(FP_ACTION)PSC_registry_delete_value, root, path, value, txt_actions_add_reg_value, txt_actions_delete_reg_value },
#define OS_SERVICE_ADD(service, d_name, binname) {
(FP_ACTION)PSC_install_os_service,(FP_ACTION)PSC_remove_os_service, service, d_name, binname, txt_actions_conf_osservice, txt_actions_remove_osservice },
#define TCP_SERVICE_ADD(service, port, proto){
(FP_ACTION)PSC_add_services_entry,(FP_ACTION)NULL, service, port, proto, txt_actions_conf_ipservice, txt_action_ipnodelete },
#define ODBC_DELETE_TRANSLATOR(translator){
(FP_ACTION)PSC_odbc_uninstall_translator,(FP_ACTION)NULL, translator, NULL, NULL, txt_actions_odbc_remove_trans, txt_actions_skip_file },
#define ODBC_DELETE_DRIVER(desc){
(FP_ACTION)PSC_odbc_uninstall_driver,(FP_ACTION)NULL, desc, NULL, NULL, txt_actions_odbc_rem_driver, txt_actions_skip_file },
#define ODBC_DELETE_CORE(comp){
(FP_ACTION)PSC_odbc_uninstall_core,(FP_ACTION)NULL, comp, NULL, NULL, txt_actions_odbc_remove_core, txt_actions_skip_file },
#define ODBC_INSTALL_CORE(comp, filelist){
(FP_ACTION)PSC_odbc_install_core,(FP_ACTION)NULL, comp, filelist, NULL, txt_actions_odbc_install_core, txt_actions_skip_file },
#define ODBC_INSTALL_DRV(desc, filelist){
(FP_ACTION)PSC_odbc_install_driver,(FP_ACTION)PSC_odbc_uninstall_driver, desc, filelist, NULL, txt_actions_install_driver, txt_actions_odbc_rem_driver },
#define ODBC_INSTALL_TRANSLATOR(translator, filelist){
(FP_ACTION)PSC_odbc_install_translator,(FP_ACTION)PSC_odbc_uninstall_translator, translator, filelist, NULL, txt_actions_install_trans, txt_actions_odbc_remove_trans },
#define END_ACTIONS {
NULL, NULL, NULL, NULL, NULL, 0L, 0L }};
We end up with a pseudo-defenition language. This allows us to create a header file for the engine which might look like:
#include "def_macros.h" /* header file containing the above struct's and macros */
#include "ibinstall.h" /* header file containing the external defines for the options */
DEFINE_GROUP(INTERBASE)
MEMBER(IB_SERVER)
MEMBER(IB_CLIENT)
MEMBER(IB_CMD_TOOLS)
MEMBER(IB_GUI_TOOLS)
MEMBER(IB_DOC)
MEMBER(IB_EXAMPLES)
MEMBER(IB_DEV)
MEMBER(IB_ODBC)
END_DEFINE_GROUP
DEFINE_GROUP(IB_CMD_TOOLS)
MEMBER(IB_CMD_TOOLS_DB_MGMT)
MEMBER(IB_CMD_TOOLS_USR_MGMT)
MEMBER(IB_CMD_TOOLS_DB_QUERY)
END_DEFINE_GROUP
DEFINE_GROUP(IB_GUI_TOOLS)
MEMBER(IB_GUI_TOOLS_DB_MGMT)
MEMBER(IB_GUI_TOOLS_DB_QUERY)
END_DEFINE_GROUP
DEFINE_GROUP(IB_EXAMPLES)
MEMBER(IB_EXAMPLE_API)
MEMBER(IB_EXAMPLE_DB)
END_DEFINE_GROUP
DEFINE_DEPENDENCY(DEPEND_CAN_INSTALL_CLIENT)
CHECK_NOT_CLASSIC
END_DEFINE_DEPENDENCY
DEFINE_DEPENDENCY(DEPEND_CAN_INSTALL_SERVER)
CHECK_PRIV
CHECK_NOT_CLASSIC
CHECK_SERVER_NOT_RUNNING
END_DEFINE_DEPENDENCY
DEFINE_DEPENDENCY(DEPEND_IB_CLIENT)
CHECK_NOT_CLASSIC
CHECK_ELEMENT(IB_CLIENT)
END_DEFINE_DEPENDENCY
DEFINE_DEPENDENCY(DEPEND_IB_DEV_CLIENT)
CHECK_ELEMENT(IB_CLIENT)
CHECK_ELEMENT(IB_DEV)
END_DEFINE_DEPENDENCY
DEFINE_DEPENDENCY(DEPEND_IB_SERVER)
CHECK_ELEMENT(IB_CLIENT)
END_DEFINE_DEPENDENCY
ACTIONS(IB_SERVER)
/* Common stuff necessary for setup to function on all platforms */
FILE_COPY_SYS_NO_OVERWRITE("setupapi.dll")
FILE_COPY_SYS_NO_OVERWRITE("cfgmgr32.dll")
FILE_COPY_SHARE_SYSTEM_NO_DEL("msvcrt.dll")
FILE_COPY_SHARE_SYSTEM_NO_DEL("odbccp32.dll")
FILE_COPY_SHARE_SYSTEM_NO_DEL("odbcint.dll")
FILE_COPY_VER_CHECK("ibinstall.dll")
FILE_COPY_VER_CHECK("ibuninst.exe")
/* Server actions */
FILE_COPY_SHARE_SYSTEM("gds32.dll")
FILE_COPY_SHARE("bin\\gdsintl.dll")
FILE_COPY("bin\\gstat.exe")
FILE_COPY_SHARE("bin\\ibguard.exe")
FILE_COPY_SHARE("bin\\iblicense.exe")
FILE_COPY_SHARE("bin\\iblicense.dll")
FILE_COPY("bin\\iblockpr.exe")
FILE_COPY_SHARE("bin\\ibserver.exe")
FILE_COPY("bin\\instreg.exe")
FILE_COPY("bin\\instsvc.exe")
FILE_COPY_SHARE("bin\\regcfg.dll")
FILE_COPY_VER_CHECK("bin\\regcfg.exe")
FILE_COPY("bin\\ibserver.cnt")
FILE_COPY("bin\\ibserver.hlp")
FILE_COPY("bin\\perform.cnt")
FILE_COPY("bin\\perform.hlp")
FILE_COPY_VER_CHECK("lib\\ib_udf.dll")
FILE_COPY_SHARE_SYSTEM_NO_DEL("mfc42.dll")
FILE_COPY("ibconfig")
FILE_COPY_SHARE("interbase.msg")
FILE_COPY_NO_OVERWRITE("isc4.gdb")
FILE_COPY_NO_OVERWRITE("isc4.gbk")
FILE_COPY("license.txt")
FILE_COPY("readme.txt")
FILE_COPY("Release_Notes.pdf")
REGISTRY_ADD_KEY("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp", "REG_SZ")
REGISTRY_ADD_KEY("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase", "REG_SZ")
REGISTRY_ADD_KEY("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion", "REG_SZ")
REGISTRY_ADD_VALUE("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\RootDirectory", ISC_INSTALL_DYN_VALUE"\\")
REGISTRY_ADD_VALUE("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\DefaultMode", "-r")
REGISTRY_ADD_VALUE("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\ServerDirectory",ISC_INSTALL_DYN_VALUE"\\bin\\")
REGISTRY_ADD_VALUE("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\Version","WI-V5.5.0")
REGISTRY_ADD_VALUE("HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\GuardianOptions","1")
OS_SERVICE_ADD("InterBaseServer", "InterBase Server", "ibserver.exe")
OS_SERVICE_ADD("InterBaseGuardian", "InterBase Guardian", "ibguard.exe")
TCP_SERVICE_ADD("gds_db", "3050", "tcp")
END_ACTIONS
#include "undef_macros.h"/* header file which undefines the macros from def_macros.h */
This particular defenition file would be preprocessed into structures simular to shown below:
static const member INTERBASE_MEMBERS[]={
IB_SERVER,
IB_CLIENT,
IB_CMD_TOOLS,
IB_GUI_TOOLS,
IB_DOC,
IB_EXAMPLES,
IB_DEV,
IB_ODBC,
0};
static const member IB_CMD_TOOLS_MEMBERS[]={
IB_CMD_TOOLS_DB_MGMT,
IB_CMD_TOOLS_USR_MGMT,
IB_CMD_TOOLS_DB_QUERY,
0};
static const member IB_GUI_TOOLS_MEMBERS[]={
IB_GUI_TOOLS_DB_MGMT,
IB_GUI_TOOLS_USR_MGMT,
IB_GUI_TOOLS_DB_QUERY,
0};
static const depend DEPEND_CAN_INSTALL_CLIENT[]={
PSC_check_classic_server,0,NULL,0,
NULL,0,NULL,0};
static const depend DEPEND_CAN_INSTALL_SERVER[]={
PSC_check_classic_server,0,NULL,0,
PSC_server_not_running,0,PSC_server_not_running,0,
NULL,0,NULL,0};
static const depend DEPEND_IB_CLIENT[]={
UTIL_check_dependency,IB_CLIENT,NULL,0,
NULL,0,NULL,0};
static const depend DEPEND_IB_DEV_CLIENT[]={
UTIL_check_dependency,IB_CLIENT,NULL,0,
UTIL_check_dependency,IB_DEV,NULL,0,
NULL,0,NULL,0};
static const COMPONENT components[]={
INTERBASE,INTERBASE_MEMBERS,0,
IB_SERVER,0,DEPEND_CAN_INSTALL_SERVER,
IB_CLIENT,0,DEPEND_CAN_INSTALL_CLIENT,
IB_CMD_TOOLS,IB_CMD_TOOLS_MEMBERS,0,
IB_CMD_TOOLS_DB_MGMT,0,DEPEND_IB_CLIENT,
IB_CMD_TOOLS_USR_MGMT,0,DEPEND_IB_CLIENT,
IB_CMD_TOOLS_DB_QUERY,0,DEPEND_IB_CLIENT,
IB_GUI_TOOLS,IB_GUI_TOOLS_MEMBERS,0,
IB_GUI_TOOLS_DB_MGMT,0,DEPEND_IB_CLIENT,
IB_GUI_TOOLS_USR_MGMT,0,DEPEND_IB_CLIENT,
IB_GUI_TOOLS_DB_QUERY,0,DEPEND_IB_CLIENT,
IB_DOC,0,DEPEND_IB_CLIENT,
IB_EXAMPLES,0,DEPEND_IB_DEV_CLIENT,
IB_DEV,0,DEPEND_IB_CLIENT,
IB_ODBC,0,DEPEND_IB_CLIENT,
0,0,0};
static const action IB_SERVER_ACTIONS[]={
PSC_file_copy_no_overwrite, PSC_file_delete,"isc4.gdb", NULL,NULL,1,
PSC_file_copy_no_overwrite, PSC_file_delete,"isc4.gbk", NULL,NULL,1,
PSC_file_copy, PSC_file_delete, "interbase.msg",NULL,NULL,1,
PSC_file_copy, PSC_file_delete, "ibconfig",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "bin\\ibserver.exe",NULL,NULL,1,
PSC_file_copy, PSC_file_delete, "bin\\ibserver.hlp",NULL,NULL,1,
PSC_file_copy, PSC_file_delete, "bin\\ibserver.cnt",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "bin\\ibguard.exe",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "bin\\iblicense.exe",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "bin\\iblicense.dll",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "bin\\regcfg.exe",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "bin\\regcfg.dll",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "bin\\gdsintl.dll",NULL,NULL,1,
PSC_file_copy, PSC_file_delete, "bin\\gstat.exe",NULL,NULL,1,
PSC_file_copy, PSC_file_delete, "bin\\iblockpr.exe",NULL,NULL,1,
PSC_file_copy_ver_check, PSC_file_delete, "lib\\ib_udf.dll",NULL,NULL,1,
PSC_reg_add_key, PSC_reg_del_key, "HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\",0,0,
PSC_reg_add_value, PSC_reg_del_value, "HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\RootDir","",0,
PSC_reg_add_value, PSC_reg_del_value, "HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\ServerDir","bin",0,
PSC_reg_add_value, PSC_reg_del_value, "HKEY_LOCAL_MACHINE","SOFTWARE\\InterBase Corp\\InterBase\\CurrentVersion\\Version","WI-V5.1.1",0,
NULL,NULL,NULL,NULL,NULL,0};
So, our install definition language is internally converted to static arrays of actions and dependencies, which can easily be parsed by our code. Note: we will have similar macros in the implementation to allow us to parse parse these arrays.
This design allows us to easily modify the actions takes by the install engine, without having to modify any of the basic algorithms. This is consistent with the original premise that this feature would be designed for use with other InterBase products such as InterClient.
Note: There would be once such definition file for every product on every platform, since this file defines the actions needed in order to install.
The only other internal data structures needed is a structure for the messages, and the structure of the uninstall file. Note, the messages the install API uses can not be stored in the interbase.msg file and accessed through the InterBase message mechanism. This is because when we are using the install engine, we are in the process of installing, or uninstalling, InterBase, therefore we can not be assured that the InterBase message file is present. Furthermore, since this feature is designed to be used to install other products, such as InterClient, we have no assurances that the messages file exists on the source path.
Lastly this feature is used to install, so we should make it self contained, thus alleviating the need to setup an environment in which the install program will function. Therefore, the messages should be stored within the install engine. This can be accomplished by defining a message structure, and initializing it in a header file. Note, this does make it more difficult to localize this feature, but if all messages are stored in one file, the amount of work needed to translate will be minimized.
The messages and message constants will be defined and initialized as follows:
#define ISC_INSTALL_ MAX_MESSAGE_LEN 300
typedef char TEXTSTR[ISC_INSTALL_MAX_MESSAGE_LEN];
typedef TEXTSTR* PTEXTSTR;
Then we define as many text tables as necessary in the following manner.
static TEXTSTR error_messages[txt_error_last_string-txt_error_first_string+1]=
{
"Warning message -n
.
.
.
"Warning message -1
"Success ",
"This is error message 1 ",
"This is error message 2 ",
.
.
.
"This is the last error message "
};
typedef struct text_table
{
STRING_ID first_id;
STRING_ID last_id;
STRING_ID access_offset; /* This is to be used to access the element in the array */
TEXT* tbl_description; /* This is primarely for use by makemsg */
PTEXTSTR list; /* This is an array of text resources */
} STRTABLE, *PSTRTABLE;
Then define an array that will hold the information about the tables so we can obtain it quickly.
/* Define an array to acces each of the messages tables */
static const STRTABLE text_tables[TXT_NUM_OF_TABLES]=
{
/* First of all goes error messages table */
{
txt_error_first_string,
txt_error_last_string,
txt_error_first_string,
ERR_SECTION,
error_messages
},
/* Now options array */
{
txt_options_first_string,
txt_options_last_string,
txt_options_first_string,
OPT_SECTION,
options_strings
},
/* Now actions text */
{
txt_actions_first_string,
txt_actions_last_string,
txt_actions_first_string,
ACT_SECTION,
actions_strings
}
};
#define isc_install_success 0
#define isc_install_message1 1
#define isc_install_message2 2
. .
. .
. .
#define isc_install_last_messsage 199
#define isc_install_first_message Warning message -n
In addition to this we will provide a format for the use to create a file called ibinstall.msg which would contain the translated messages. At runtime the install engine will look for this file when the setup program developer calls isc_install_load_external_file, read it into memory, and point msgs to it. If the file is not loaded, then msgs has already been initialized to point to english_msgs. This way, the install engine will always have the english messages, and if the developer wishes to provide the translated ones, the install engine will use those.
Detailed Algorithm
Each function will depend on several internal functions, the algorithm for which will be written at a later time. For now it is sufficient to say that these functions will be simple low level functions, and that they perform the action implied by their name. These functions are shown in bold for the sake of distinguishing them from other function calls.
The detailed algorithm for the eight API functions is as follows:
MSG_NO isc_install_set_option(OPTIONS_HANDLE* phandle, OPT option) begin if (UTIL_check_handle_ref(phandle) OR UTIL_check_handle(*phandle)) then return (error invalid handle) if not (UTIL_valid_option(option)) then return (error invalid option) return UTIL_add_option(phandle, option) end MSG_NO isc_install_unset_option(OPTIONS_HANDLE* phandle, OPT option) begin if(UTIL_check_handle_ref(phandle) OR UTIL_check_handle(*phandle)) then return (error invalid handle) return UTIL_delete_option(phandle, option) end MSG_NO isc_install_clear_options(OPTIONS_HANDLE* phandle) begin if (UTIL_check_handle_ref(phandle) OR UTIL_check_handle(*phandle)) then return (error invalid handle) return UTIL_free_options_list(phandle) end MSG_NO isc_install_precheck(OPTIONS_HANDLE handle, char *source_path, char *dest_path) begin if (handle=null) return (error invalid handle); if (UTIL_check_handle(handle)) then return (warning invalid handle) if(PSC_check_system(handle)) then return (error invalid operating system) if(source_path not null OR source_path not empty) begin if( PSC_check_source(source_path)) return source_path error end if(dest_path not null) begin if(dest_path is empty string) begin if(PSC_suggest_destination(dest_path)) return error else return success else /* dest_path is not empty */ begin if(PSC_check_destination(dest_path) return dest_path error if(PSC_get_disk_free_space(pspace_avail)) return can not get free space if(UTIL_calaculate_diskspace(pspace_need)) return can not calculate space needed if(space_need > space_avail) return error not enough disk space end end return UTIL_check_dependency(handle) /*********************************************** * UTIL_check_dependency will parse the dependency * tree specified in the defenition languag, and * call PSC_classic_not_found, if necessary. * * This is the contents of PSC_classic_not_found * * if GDS32.DLL does NOT have any version info, * then this is a classic server, rather * than a super client, thus abort. * * Check the Software\Borland\InterBase in the * registry for the RootDir, and use this for * the path to GDS32.DLL if (GetRegistryValue("HKEY_LOCAL_MACHINE", "Software\Borland\InterBase", "RootDir", &buff, length(buff)) then if not (GetVerInfo(buff+"\bin","GDS32.dll",...)) then return (error classic server found) * Check the Software\InterBase Corp.\InterBase in the * registry for the RootDir, and use this for * the path to GDS32.DLL * if (GetRegistryValue("HKEY_LOCAL_MACHINE", "Software\InterBase Corp.\InterBase", "RootDir", &buff, length(buff)) then if (NOT try_version(buff, "gds32.dll")) then return (error classic server found) * Check the windows system directory for GDS32.DLL if (NOT try_version(GetSystemDirectory(...),"GDS32.dll",...)) then return (error classic server found) * end of PSC_classic_not_found() *********************************************************/ end MSG_NO isc_install_execute(OPTIONS_HANDLE handle, TEXT *source_path, TEXT *dest_path, FP_STATUS *fp_status, void *status_arg, FP_ERROR *fp_error, void *error_arg, TEXT *uninstall_file_name) ProcessError(err) begin open(log) write(err) flush(log) close(log) return (fp_error (err) ) end begin if(source_path is null OR source_path is empty OR dest_path is null OR dest_path is empty) return error path not valid if(dest_path not exist) if (UTIL_make_directory(dest_path)) then return (error can not create dest dir) err=isc_install_precheck(handle, source_path, dest_path) if (err > 0)then log("error can not perform install error (err)") return (error can not perform install error (err)) /* Walk the list of options and create a queue of files * to be copied. Once this is done, commit the queue, * performing the actual copying of files and the creating * of any registry entries, as specified in the defenition * language file. This is done with * the use of the Windows Setup API. If any errors occur * during copying, process them using ProcessError. Also, * use fp_status to keep users appraised of the copying * status. Install the guardian service on WinNT or add the * Guardian to the Run line on Win95 */ log(copying files) fp_status(30) if not (UTIL_process_options(handle) then return (error could not process options) if not (UTIL_perform_actions(handle)) then begin UTIL_unperform_actions(handle) return (error could not copy files) end log(streaming options) do uninstall_file_name=UTIL_write_options(handle) if not (uninstall_file_name) then ret=ProcessError(error could not create uninstall file) if (ret=ABORT) then return (error could not create uninstall file) while (ret=RETRY) UTIL_move_text_log(UTIL_get_temp_dir()+ib_install.log,dest_path) UTIL_free_options_list(handle) return (success) end MSG_NO isc_uninstall_precheck(char *uninstall_file_name) var handle; end begin if (UTIL_read_options(phandle, uninstall_file_name)) then return (error reading log file) if(PSC_check_system(handle)) return (os not supported) if (UTIL_check_uninstall_dependencies(handle)) then return dependancy status; UTIL_free_options_list(phandle) return (success) end MSG_NO isc_uninstall_execute(TEXT *uninstall_file_name, FP_STATUS *fp_status, void *status_arg, FP_ERROR *fp_error, void *error_arg) var handle end ProcessError(err) begin open(log) write(err) close(log) return (fp_error (err) ) end begin err=isc_uninstall_precheck(uninstall_file_name) if (err > 0)then return (uninstall precheck error) if not (UTIL_read_options(phandle, uninst_file)) then return (error unable to read uninstall data) PSC_check_system(handle) /* We do not check the return value here, just fill out the handle */ /* Walk the list of options and create a queue of files * to be uninstalled. Once this is done, commit the queue, * performing the actual uninstalling. This is done with * the use of the Windows Setup API. If any errors occur * during uninstallation, process them using ProcessError. Also, * use fp_status to keep sers appraised of the copying * status. Uninstall the guardian.*/ if not (UTIL_process_options(handle, list) then return (error could not process options) if not (UTIL_unperform_actions(handle, list)) then return (error could not uninstall files) UTIL_free_options_list(handle) return(success) end MSG_NO isc_install_get_message(handle, MSG_NO msg_no, char *msg, int msg_len) begin if(msg=null or msg_len=0) return invalid param if(UTIL_check_handle(handle)) return invalid handle if (TXT_get_text(handle, msg_no, msg, msg_len)) then return (get message error) return (success) end MSG_NO isc_install_load_external_text(TEXT* external_path) begin if(ibinstall.msg exist at external_path) then TXT_load_external_file() else return can not load the external file end end
New/Affected modules
This feature will be implemented in a new component called INSTALL. This new component will contain the following modules:
Module Name | Description |
---|---|
def_macros.h | Contains pseudo language definitions. |
undef_macros.h | Undefs the pseudo language defines. |
ibinstall.h | This header file contains all the public, external defines, and data structures, along with the prototypes for interface.c. This is the file which a VAR will need to use to implement an install application using the InterBase Install API. |
ibinstall.c | The main module containing the entry points for the API. Each entry point implements the basic non platform specific algorithm, calls the generic utility functions from util.c for the lower level implementation details, and the platform specific functions from windows.c. |
inst_common.h | The main header file containing the data structures listed above in the Internal Data Structures section. |
inst_text.c | Contains default install error, warning messages, option names and escriptions, actions descriptions and messages that are logged out to the ib_install.log file. This module also contains the intrefaces provided for other modules to manipulate the text information. Those interfaces are also used by the MAKEMSG utility. |
interbase.h | This file contains the pseudo-defenition language which describes the groups of components, the components, and the actions to be taken for each component. This file will be included by util.c. |
util_proto.h | The function prototypes for the non platform specific utility functions. |
util.c | This module contains worker routines which will isolate the lower level details from the main code. For example there will be routines for adding, finding and removing nodes from the linked list of options, as well as non platform specific code for processing the options list, and reading the actions array from prod_description.h |
util.h | Contains static functions prototypes for UTIL and necessary typedefs and defines |
psc_proto.h | The function prototypes for the Platform Specific Code in windows.c for the time being. When this feature will be ported we will need to create a new .c file implementing the functions for the platform being ported. |
psc_windows.c | This module contains the windows specific implementation of some of the worker functions, such as the install_precheck and the install_execute functions. These will vary greatly from platform to platform, thus having an implementation for each one allows us to address the needs of each platform without cluttering the code with lots of #ifdef's. |
psc_windows.h | Contains static functions prototypes for PSC and necessary typedefs and defines |
The contents of each of the prototypes are as follows:
ibinstall.h:
MSG_NO isc_install_set_option(OPTIONS_HANDLE* phandle, OPT option) MSG_NO isc_install_unset_option(OPTIONS_HANDLE* phandle, OPT option) MSG_NO isc_install_clear_options(OPTIONS_HANDLE* phandle) MSG_NO isc_install_precheck(OPTIONS_HANDLE handle, char *source_path, char *dest_path) MSG_NO isc_install_execute(OPTIONS_HANDLE handle, char *source_path, char *dest_path, FP_STATUS fp_status, void *status_arg, FP_ERROR fp_error, void *error_arg, char *uninst_file_name) MSG_NO isc_uninstall_precheck(char *uninstall_file_name) MSG_NO isc_uninstall_execute(char *uninstall_file_name, FP_STATUS fp_status, void *status_arg, FP_ERROR fp_error, void *error_arg) MSG_NO isc_install_get_message(int msg_no, char *msg, int msg_len) MSG_NO isc_install_get_info(OPT option, int info_type, void* info_buf, unsigned int buf_len)
util_proto.h:
MSG_NO UTIL_add_option MSG_NO UTIL_calculate_diskspace MSG_NO UTIL_check_dependance MSG_NO UTIL_check_handle MSG_NO UTIL_check_handle_ref MSG_NO UTIL_delete_option MSG_NO UTIL_find_option MSG_NO UTIL_free_options_list MSG_NO UTIL_make_directory MSG_NO UTIL_move_text_log MSG_NO UTIL_perform_actions MSG_NO UTIL_process_options MSG_NO UTIL_read_options MSG_NO UTIL_unperform_actions MSG_NO UTIL_valid_option MSG_NO UTIL_write_options void UTIL_log_message void UTIL_log_action_text MSG_NO UTIL_load_messages /* Currently not implemented */
psc_proto.h:
/* Functions required to inplement on all platforms shown only. Other functions may not be present. Some other functions may be present instead */ MSG_NO PSC_call_fp_error MSG_NO PSC_call_fp_status MSG_NO PSC_check_system MSG_NO PSC_classic_not_found MSG_NO PSC_check_destination MSG_NO PSC_check_source MSG_NO PSC_suggest_destination MSG_NO PSC_get_free_diskspace MSG_NO PSC_server_running MSG_NO PSC_check_permissions(void) MSG_NO PSC_get_temp_dir MSG_NO PSC_move_file MSG_NO PSC_get_text_string /* Loads a test string from a file or resources */
inst_text_proto.h:
MSG_NO TXT_load_external_file MSG_NO TXT_get_text MSG_NO TXT_get_action_text MSG_NO TXT_get_table_info
Testing Considerations
This feature is a new API, therefore in order to test it thoroughly each functions must be tested for boundary conditions, and invalid input, as well as testing all the functions in correct and incorrect order of execution.
MAKEMSG utility has an extra option in the dev build. Option -l <textfile> allows to load the ibinstall.msg file from the current directory and then to extract the loaded text out to the specified textfile. A test engineer can then diff the text file with a text file from which the ibinstall.msg file was created.