| /*------------------------------------------------------------------------- |
| * |
| * support.c |
| * BeOS Support functions |
| * |
| * Copyright (c) 1999-2001, Cyril VELTER |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| /* Support Globals */ |
| port_id beos_dl_port_in = 0; |
| port_id beos_dl_port_out = 0; |
| sem_id beos_shm_sem; |
| |
| /* Global var containing the postgres path */ |
| extern char my_exec_path[]; |
| |
| |
| /* Shared library loading doesn't work after fork in beos. The solution is to use an exact |
| copy of the process and use it to perform the loading, then just map the Text and Data segment |
| of the add-on in our address space. Both process must have the exact same memory mapping, so |
| we use the postgres executable. When it's lauched with the -beossupportserver parameter, the |
| postgres executable just run a loop to wait command on a port. Its only action is to load the addon, |
| the beos_dl_open will then remap the good areas in the backend address space. */ |
| |
| |
| image_id |
| beos_dl_open(char *filename) |
| { |
| image_id im; |
| |
| /* If a port doesn't exist, lauch support server */ |
| if ((beos_dl_port_in <= 0) || (beos_dl_port_out <= 0)) |
| { |
| /* Create communication port */ |
| beos_dl_port_in = create_port(50, "beos_support_in"); |
| beos_dl_port_out = create_port(50, "beos_support_in"); |
| |
| |
| if ((beos_dl_port_in <= 0) || (beos_dl_port_out <= 0)) |
| { |
| elog(WARNING, "error loading BeOS support server: could not create communication ports"); |
| return B_ERROR; |
| } |
| else |
| { |
| char Cmd[4000]; |
| |
| /* Build arg list */ |
| sprintf(Cmd, "%s -beossupportserver %d %d &", my_exec_path, (int) beos_dl_port_in, (int) beos_dl_port_out); |
| |
| /* Lauch process */ |
| system(Cmd); |
| } |
| } |
| |
| /* Add-on loading */ |
| |
| /* Send command '1' (load) to the support server */ |
| write_port(beos_dl_port_in, 1, filename, strlen(filename) + 1); |
| |
| /* Read Object Id */ |
| read_port(beos_dl_port_out, &im, NULL, 0); |
| |
| /* Checking integrity */ |
| if (im < 0) |
| { |
| elog(WARNING, "could not load this add-on"); |
| return B_ERROR; |
| } |
| else |
| { |
| /* Map text and data segment in our address space */ |
| char datas[4000]; |
| int32 area; |
| int32 resu; |
| void *add; |
| |
| /* read text segment id and address */ |
| read_port(beos_dl_port_out, &area, datas, 4000); |
| read_port(beos_dl_port_out, (void *) &add, datas, 4000); |
| /* map text segment in our address space */ |
| resu = clone_area(datas, &add, B_EXACT_ADDRESS, B_READ_AREA | B_WRITE_AREA, area); |
| if (resu < 0) |
| { |
| /* If we can't map, we are in reload case */ |
| /* delete the mapping */ |
| resu = delete_area(area_for(add)); |
| /* Remap */ |
| resu = clone_area(datas, &add, B_EXACT_ADDRESS, B_READ_AREA | B_WRITE_AREA, area); |
| if (resu < 0) |
| elog(WARNING, "could not load this add-on: map text error"); |
| } |
| |
| /* read text segment id and address */ |
| read_port(beos_dl_port_out, &area, datas, 4000); |
| read_port(beos_dl_port_out, (void *) &add, datas, 4000); |
| /* map text segment in our address space */ |
| resu = clone_area(datas, &add, B_EXACT_ADDRESS, B_READ_AREA | B_WRITE_AREA, area); |
| if (resu < 0) |
| { |
| /* If we can't map, we are in reload case */ |
| /* delete the mapping */ |
| resu = delete_area(area_for(add)); |
| /* Remap */ |
| resu = clone_area(datas, &add, B_EXACT_ADDRESS, B_READ_AREA | B_WRITE_AREA, area); |
| if (resu < 0) |
| elog(WARNING, "could not load this add-on: map data error"); |
| } |
| |
| return im; |
| } |
| } |
| |
| void |
| beos_dl_sym(image_id im, char *symname, void **fptr) |
| { |
| /* Send command '3' (get symbol) to the support server */ |
| write_port(beos_dl_port_in, 3, symname, strlen(symname) + 1); |
| write_port(beos_dl_port_in, im, NULL, 0); |
| |
| /* Read sym address */ |
| read_port(beos_dl_port_out, (int32 *) (fptr), NULL, 0); |
| |
| if (fptr == NULL) |
| elog(WARNING, "loading symbol \"%s\" failed", symname); |
| } |
| |
| status_t |
| beos_dl_close(image_id im) |
| { |
| /* unload add-on */ |
| int32 resu; |
| |
| write_port(beos_dl_port_in, 2, &im, 4); |
| read_port(beos_dl_port_out, &resu, NULL, 0); |
| return resu; |
| } |
| |
| /* Main support server loop */ |
| |
| void |
| beos_startup(int argc, char **argv) |
| { |
| if (strlen(argv[0]) >= 10 && !strcmp(argv[0] + strlen(argv[0]) - 10, "postmaster")) |
| { |
| /* |
| * We are in the postmaster, create the protection semaphore for |
| * shared mem remapping |
| */ |
| beos_shm_sem = create_sem(1, "beos_shm_sem"); |
| } |
| |
| if (argc > 1 && strcmp(argv[1], "-beossupportserver") == 0) |
| { |
| /* We are in the support server, run it ... */ |
| |
| port_id port_in; |
| port_id port_out; |
| |
| /* Get back port ids from arglist */ |
| sscanf(argv[2], "%d", (int *) (&port_in)); |
| sscanf(argv[3], "%d", (int *) (&port_out)); |
| |
| /* Main server loop */ |
| for (;;) |
| { |
| int32 opcode = 0; |
| char datas[4000]; |
| |
| /* |
| * Wait for a message from the backend : 1 : load a shared object |
| * 2 : unload a shared object any other : exit support server |
| */ |
| read_port(port_in, &opcode, datas, 4000); |
| |
| switch (opcode) |
| { |
| image_id addon; |
| image_info info_im; |
| area_info info_ar; |
| void *fpt; |
| |
| /* Load Add-On */ |
| case 1: |
| |
| /* Load shared object */ |
| addon = load_add_on(datas); |
| |
| /* send back the shared object Id */ |
| write_port(port_out, addon, NULL, 0); |
| |
| /* Get Shared Object infos */ |
| get_image_info(addon, &info_im); |
| |
| /* get text segment info */ |
| get_area_info(area_for(info_im.text), &info_ar); |
| /* Send back area_id of text segment */ |
| write_port(port_out, info_ar.area, info_ar.name, strlen(info_ar.name) + 1); |
| /* Send back real address of text segment */ |
| write_port(port_out, (int) info_ar.address, info_ar.name, strlen(info_ar.name) + 1); |
| |
| |
| /* get data segment info */ |
| get_area_info(area_for(info_im.data), &info_ar); |
| /* Send back area_id of data segment */ |
| write_port(port_out, info_ar.area, info_ar.name, strlen(info_ar.name) + 1); |
| /* Send back real address of data segment */ |
| write_port(port_out, (int) info_ar.address, info_ar.name, strlen(info_ar.name) + 1); |
| break; |
| /* UnLoad Add-On */ |
| case 2: |
| |
| /* |
| * Unload shared object and send back the result of the |
| * operation |
| */ |
| write_port(port_out, unload_add_on(*((int *) (datas))), NULL, 0); |
| break; |
| /* Cleanup and exit */ |
| case 3: |
| |
| /* read image Id on the input port */ |
| read_port(port_in, &addon, NULL, 0); |
| |
| /* Loading symbol */ |
| fpt = NULL; |
| |
| |
| if (get_image_symbol(addon, datas, B_SYMBOL_TYPE_TEXT, &fpt) == B_OK); |
| { |
| /* |
| * Sometime the loader return B_OK for an inexistant |
| * function with an invalid address !!! Check that the |
| * return address is in the image range |
| */ |
| |
| get_image_info(addon, &info_im); |
| if ((fpt < info_im.text) ||(fpt >= (info_im.text +info_im.text_size))) |
| fpt = NULL; |
| } |
| |
| /* Send back fptr of data segment */ |
| write_port(port_out, (int32) (fpt), NULL, 0); |
| break; |
| |
| default: |
| /* Free system resources */ |
| delete_port(port_in); |
| delete_port(port_out); |
| /* Exit */ |
| exit(0); |
| break; |
| } |
| } |
| /* Never be there */ |
| exit(1); |
| } |
| } |
| |
| |
| |
| /* The behavior of fork is broken on beos regarding shared memory. In fact |
| all shared memory areas are clones in copy on write mode in the new process. |
| |
| We need to do a remapping of these areas. Just afer the fork we performe the |
| following actions : |
| |
| * Find all areas with a name begining by SYS_V_IPC_ in our process |
| (areas created by the SYSV IPC emulation functions). The name is |
| followed by the IPC KEY in decimal format |
| |
| * For each area we do : |
| |
| * 1 : Get its name |
| * 2 : destroy it |
| * 3 : find another area with the exact same name |
| * 4 : clone it in our address space with a different name |
| |
| There is a race condition in 3-4 : if there two fork in a very short |
| time, in step 3 we might end up with two areas with the same name, and no |
| possibility to find the postmaster one. So the whole process is protected |
| by a semaphore which is acquires just before the fork and released in case |
| of fork failure or just after the end of the remapping.*/ |
| |
| void |
| beos_before_backend_startup(void) |
| { |
| /* Just before forking, acquire the semaphore */ |
| if (acquire_sem(beos_shm_sem) != B_OK) |
| exit(1); /* Fatal error, exiting with error */ |
| } |
| |
| void |
| beos_backend_startup_failed(void) |
| { |
| /* The fork failed, just release the semaphore */ |
| release_sem(beos_shm_sem); |
| } |
| |
| |
| void |
| beos_backend_startup(void) |
| { |
| char nom[50]; |
| char nvnom[50]; |
| area_info inf; |
| int32 cook = 0; |
| |
| /* Perform the remapping process */ |
| |
| /* Loop in all our team areas */ |
| while (get_next_area_info(0, &cook, &inf) == B_OK) |
| { |
| strcpy(nom, inf.name); |
| strcpy(nvnom, inf.name); |
| nom[9] = 0; |
| nvnom[5] = 'i'; |
| /* Is it a SYS V area ? */ |
| if (!strcmp(nom, "SYSV_IPC_")) |
| { |
| void *area_address; |
| area_id area_postmaster; |
| |
| /* Get the area address */ |
| area_address = inf.address; |
| /* Destroy the bad area */ |
| delete_area(inf.area); |
| /* Find the postmaster area */ |
| area_postmaster = find_area(inf.name); |
| /* Compute new area name */ |
| sprintf(nvnom, "SYSV_IPC %d", area_postmaster); |
| /* Clone it at the exact same address */ |
| clone_area(nvnom, &area_address, B_CLONE_ADDRESS, B_READ_AREA | B_WRITE_AREA, area_postmaster); |
| } |
| } |
| |
| /* remapping done release semaphore to allow other backend to startup */ |
| |
| release_sem(beos_shm_sem); |
| } |