int kubl_main (int argc, char **argv, int called_as_service, DWORD * errptr) { int i, exit_after_options = 0, started_with_itself = 0; int read_from_rebuilt_database = 0; /* For -R option. */ int dump_for_recovery = 0; /* For -D option. */ char *empty = ""; char *mode = empty; char *addr = KUBL_DEFAULT_PORT; char *service_name = (called_as_service ? argv[0] : NULL); char *s; #ifdef PMN_LOG log_open_fp (stderr, LOG_DEBUG, L_MASK_ALL, L_STYLE_GROUP|L_STYLE_TIME); log_open_file ("wi.err", LOG_DEBUG, L_MASK_ALL, L_STYLE_GROUP | L_STYLE_TIME); #endif /* If not overridden with any arguments specified with StartService, (i.e. either there are no args at all, or there is just -S) then use the permanent arguments (saved into wisvc_Main_G_argv) got from the original BinaryPath constructed in wisvc_Handle_I_and_J_options */ if (called_as_service && (((argc == 2) && !strncmp (argv[1], "-S", 2)) || (argc < 2)) ) { if (argc == 2) { started_with_itself = 1; } argc = wisvc_Main_G_argc; argv = wisvc_Main_G_argv; } /* For debugging log_error ( "kubl_main called with argc=%d, argv[0]=%s, argv[1]=%s, called_as_service=%d pid=%d", argc, argv[0], ((argc > 1) ? argv[1] : "NULL"), called_as_service, getpid()); */ /* If coming from KublServiceStart then argv vector seems to be NOT terminated by NULL (contrary to what MS documentation claims), so let's check that i stays smaller than argc. */ for (i = 1; i < argc; i++) { s = argv[i]; if ('-' == *s) { if (mode == empty) { mode = s; } switch (*(s + 1)) { #ifdef WIN32 case 'I': case 'J': /* Install to services. I =with autostart */ { int stat; if (mode == s) { mode = empty; } if (called_as_service) { break; } /* Ignore in service. */ stat = wisvc_Handle_I_and_J_options (argc, argv, s, i, ('I' == *(s + 1))); kubl_main_exit (stat); } case 'S': /* Start a previously installed service. */ { /* Might be on the same command line as -J (or -I) */ /* in which case Handle_I_and_J_options has the responsibility to start it, directly from CreateKublServices */ int j, stat; char *service_name = (*(s + 2) ? (s + 2) : WISVC_DEFAULT_SERVICE_NAME); SC_HANDLE schandle; if (mode == s) { mode = empty; } if (called_as_service) { started_with_itself = 1; break; } /* Ignore in service. */ /* We COULD copy the rest of argv vector one left, squashing -S itself out of the existence, but we DON'T do it, as -S option is an important signal to the started server that it was started with wi.exe itself, not by clicking the start button in services icon of Control Panel. The difference is that with the latter starting way the service is reported to be successfully started almost immediately (before the initializations and log roll forward) while the wi -S way of starting ensures that after the wi -S command exits we know that the service is really started all the way up and listening. This feature is needed in few test scripts that turn Kubl server on and off, on and off. */ /* for(j=i; j < argc; j++) { argv[j] = argv[j+1]; } argc--; */ schandle = wisvc_OpenKublService (argv, service_name, "start", (GENERIC_EXECUTE | GENERIC_READ)); /* Returns 1 if started for sure, 0 if failed or unsure. */ stat = wisvc_StartKublService (argc, argv, schandle, service_name, argv[0], 0); /* Returns exit status 0 (= success) if certainly started, */ kubl_main_exit (!stat); /* otherwise 1 (= failure). */ break; } case 'U': /* Uninstall from Services. */ { char *service_name = (*(s + 2) ? (s + 2) : WISVC_DEFAULT_SERVICE_NAME); if (mode == s) { mode = empty; } wisvc_UninstallKublService (argv, service_name); exit_after_options = 1; break; } #endif case 'd': #ifdef DBG_BLOB_PAGES_ACCOUNT f_backup_dump = 1; #endif is_db_to_log = 1; break; case 'D': { dump_for_recovery = (i + 1); goto out; } case 'R': { read_from_rebuilt_database = 1; f_read_from_rebuilt_database = 1; break; } case 'j': ob_just_report = 1; /* fall to the next */ case 'r': { if (i < argc - 1) recover_file_prefix = argv[i+1]; i++; break; } case 'B': { if (i < argc - 1) backup_dirs = argv[i+1]; goto out; } case 'W': /* Change working directory. */ { int stat; if (mode == s) { mode = empty; } stat = wisvc_Handle_W_option (argc, argv, s, &i, called_as_service); if (stat) { kubl_main_exit (stat); } break; } default: /* E.g. everything else like -? -H or -h for help. */ { chil_usage (argv); kubl_main_exit (0); } } } else if (isdigit (*s)) { addr = s; } else { dbg_printf (("%s: Don't know what to do with command line argument \"%s\". Read this:\n", argv[0], s)); chil_usage (argv); kubl_main_exit (1); } } /* For loop over arguments. */ out:; if (exit_after_options) { kubl_main_exit (0); } /* If called as service and this was not started with wi -S (The -S option was not present in command line arguments) then this presumably is started with a start button from services manager of Control Panel. In that case, send SERVICE_RUNNING status immediately, so that it won't start waiting for memory initializations and log roll forwards, as its patience would not be enough for it, and it would instead falsely claim that: "Could not start the Kubl service on \\ARTAUD. Error 2186: The service is not responding to the control function." */ if (called_as_service && !started_with_itself) { wisvc_send_service_running_status (); } #ifndef WIN32 signal (SIGPIPE, SIG_IGN); #endif srv_global_init (mode); if (recover_file_prefix) { kubl_main_exit (0); } if (read_from_rebuilt_database) { kubl_main_exit (0); } if (is_db_to_log) { db_to_log (); kubl_main_exit (0); } /* If there was -D option, then dump_for_recovery is set to an index of argv one right to it, where might be one or more recovery keys. */ if (dump_for_recovery) { for (i = dump_for_recovery; i < argc; i++) { int k = atoi (argv[i]); if (k) db_recover_key (k, k); } db_crash_to_log (mode); kubl_main_exit (0); } tcpses_set_reuse_address (1); listening = PrpcListen (addr, SESCLASS_TCPIP); server_port = tcpses_get_port (listening->dks_session); if (!DKSESSTAT_ISSET (listening, SST_LISTENING)) { kubl_main_exit (1); } if (service_name) /* Started as a Windows NT service? */ { log_info ("Server started at %s as service %s, pid=%d", addr, service_name, getpid ()); } else { log_info ("Server started at %s, pid=%d", addr, getpid ()); } virtuoso_server_initialized = 1; if (!strchr (mode, 'b')) http_init_part_two (); #ifdef REPLICATION if (read_from_rebuilt_database) /* if booting from crash log, */ { /* go read the account levels from db */ repl_read_db_levels (); } repl_sync_server (NULL, NULL); #endif /* If called as Windows NT service, return now back to wisvc_KublServiceStart which in turn will call main_the_rest after it has set */ if (called_as_service) { return (0); } /* 0 = NO_ERROR */ #ifndef WIN32 signal (SIGINT, sig_catcher); signal (SIGTERM, sig_catcher); signal (SIGHUP, sig_catcher); signal (SIGQUIT, sig_catcher); #endif main_the_rest (); return 0; }
/* service_read () Used to read from a session. Handles scheduling if the session would block. Used for reading from a service thread. If the read would block, put the thread to wait. When the scheduling cycle sees input on this session the random_input_ready_action is called. This wakes up this thread and schedules it for execution on the next round The need_all argument controls whether this function may return after reading fewer than the requested number of bytes. This function always reads at least 1 byte. If the calling thread is the scheduling thread and io would block, this allows schedule and recursively blocks on all pending i/o. If the calling thread is some other thread, this disables the thread and tells the scheduler to resume this when the input is ready. Returns the number of bytes read. */ int service_read (dk_session_t * ses, char *buffer, int req_bytes, int need_all) { USE_GLOBAL int last_read = 0; int bytes = req_bytes; du_thread_t *cur_proc; /* mty NEW */ int rc; DBG_CHECK_READ_FAIL (ses); while (bytes > 0) { without_scheduling_tic (); if (!ses->dks_is_read_select_ready && ses->dks_session && ses->dks_session->ses_class != SESCLASS_STRING) { tcpses_is_read_ready (ses->dks_session, &ses->dks_read_block_timeout); if (DKSESSTAT_ISSET (ses, SST_TIMED_OUT)) rc = -1; else rc = session_read (ses->dks_session, &(buffer[last_read]), bytes); } else { if (!ses->dks_session) longjmp_splice (&(SESSION_SCH_DATA (ses)->sio_read_broken_context), 1); rc = session_read (ses->dks_session, &(buffer[last_read]), bytes); } ses->dks_is_read_select_ready = 0; restore_scheduling_tic (); if (rc == 0) PROCESS_ALLOW_SCHEDULE (); else if (rc > 0) { bytes = bytes - rc; last_read = last_read + rc; if (!need_all) { ses->dks_bytes_received += last_read; return (last_read); } } if (rc <= 0) { if (SESSTAT_ISSET (ses->dks_session, SST_INTERRUPTED)) { PROCESS_ALLOW_SCHEDULE (); } else if (SESSTAT_ISSET (ses->dks_session, SST_BLOCK_ON_READ)) { /* would block. suspend thread */ cur_proc = current_process; /* mty NEW */ if (!PROCESS_TO_DK_THREAD (cur_proc)) { /* We have a block on a server thread. We recognize it * because a server thread is not associated to a request. * The read would block the server thread. Run others and * do a recursive check_inputs to resume other threads that * may now be ready for i/o. Do a timeout round to unblock * threads waiting on timed-out futures if the select times * out. Finally retry read. */ int rc2; PROCESS_ALLOW_SCHEDULE (); rc2 = check_inputs (PASS_G & atomic_timeout, 1); if (rc2 == 0) timeout_round (PASS_G ses); } else { SESSION_SCH_DATA (ses)->sio_random_read_ready_action = unfreeze_thread_read; SESSION_SCH_DATA (ses)->sio_reading_thread = cur_proc; add_to_served_sessions (ses); semaphore_enter (cur_proc->thr_sem); } } else if (1 || /* ?? */ SESSTAT_ISSET (ses->dks_session, SST_TIMED_OUT) || SESSTAT_ISSET (ses->dks_session, SST_BROKEN_CONNECTION)) { SESSTAT_CLR (ses->dks_session, SST_OK); SESSTAT_SET (ses->dks_session, SST_BROKEN_CONNECTION); longjmp_splice (&(SESSION_SCH_DATA (ses)->sio_read_broken_context), 1); } else { ses->dks_bytes_received += last_read; ss_dprintf_2 (("Unrecognized I/O error rc=%d errno=%d in service_read.", rc, errno)); longjmp_splice (&(SESSION_SCH_DATA (ses)->sio_read_broken_context), 1); } } } ses->dks_bytes_received += last_read; return (last_read); }