int rm_init() { int i; // counter int rc; // return code // initialize mutex lock rc = pthread_mutex_init(&accInfoLock, NULL); if (rc != 0) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "failed to gain mutex lock"); return 0; } // reserve memory for status information about accounts // (maximum size is taken from account management) accInfos = (accountstatus *) malloc(config.accounts.accountManagement. maxAccountIdAmount * sizeof(accountstatus)); // initialize data explicitely so we can be sure it is not being used yet for (i = 0; i < config.accounts.accountManagement.maxAccountIdAmount; i++) { clear_account_info(i); } return 1; }
/** * Common error handling of the registration thread (internal use only). In * most cases the GUI has to be informed that the registration was not * successfull and the account information has to be cleared. * @param pos the position of the account information of the current account * @param accountId the account ID of the current account * @param acquireLock to clear the account information we need to gain the * lock - not needed if it is already locked (boolean) */ void leave_reg_thrd_with_error(int pos, int accountId, int acquireLock) { int rc; // return code // try to change the registration status at the GUI: rc = go_change_reg_status(accountId, 0); if (!rc) { // changing reg. status failed, continue anyway LOG_DEBUG(REGISTRAR_MGR_MSG_PREFIX "GUI registration" " status update failed"); } // only lock if required if (acquireLock) { // lock, because we want to write when further events may arrive rc = pthread_mutex_lock(&accInfoLock); if (rc != 0) { // failed to gain lock, we have to leave LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "mutex lock could " "not be acquired, error: %d", rc); thread_terminated(); } } // if position previously filled: if (pos != -1) { // mark account as not being used: clear_account_info(pos); } // release lock (in any case): rc = pthread_mutex_unlock(&accInfoLock); if (rc != 0) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "mutex lock could not be " "released, error %d", rc); thread_terminated(); } // exit thread: thread_terminated(); }
void proc_loop(void) { int status; static char trname[17]; int prev_total_accounts; int loop = FALSE; int bLoggedIntoTHS = FALSE; int process_log_initialized = 0; int bNormalExit = FALSE; /* ** Begin the main processing loop. This loop is executed ** until an error condition is encountered or an EXIT/ABORT ** interrupt is received. */ while ((get_next_task() != ARB_STATUS_LOGOUT)) { Est_mode = task_mode(); Proc_mode=0; /* ** Initialize task mode field for Process Logging */ switch (Est_mode) { case MODE_ACNT_EST: strncpy(proclogmode,PROCLOG_ACCNT_ESTIMATE_KEY,80); break; case REDO: strncpy(proclogmode,PROCLOG_REESTIMATE_KEY,80); break; default: strncpy(proclogmode,PROCLOG_ESTIMATE_KEY,80); break; } /* **log in to THS_Client */ if (InitSendToTHS_SocketBackend(dbadmin,arb_get_process_id()) != Success) break; bLoggedIntoTHS = TRUE; message = (THS_CHR_MSG*)calloc (1,sizeof(THS_CHR_MSG)); if(!message) { emit(BIPMOD, BIP_MEM_ALLOC_ERR,1, sizeof(THS_CHR_MSG), "send_ths_message"); break; } /* ** Create the task identifier from the process ID and the ** current time. Clear the counts of successful and errored ** estimates. */ make_task_id(); Total_successes = 0; Total_skips = 0; Total_in_error = 0; Total_accounts = 0; /* ** Fetch the list of account identifiers for the accounts ** to be processed. */ emit(BIPMOD, BIP_TASK_QUERY, MODULE,task_sql_query()); timer_set = set_perf_timer((char *)"TOTAL: charge estimation"); incr_perf_nest(); while (get_next_hq_group() != Failure) { prev_total_accounts = Total_accounts; if (get_account_ids(task_sql_query()) == Failure) { emit(BIPMOD, BIP_ACCOUNT_LIST_ERR); goto unlock_quit; } if (!process_log_initialized) { init_process_log_sys(); process_log_initialized = 1; } proc_work_upd(proclogmode,PROCLOG_PROCACC_KEY,NULL,PROCLOG_PROCACC_CV,Total_accounts,Total_successes+Total_in_error,Total_successes,Total_in_error); /* ** Load the static configuration data for the process. ** This function no-ops after being once loaded. */ set_large_db_notification_on(Thresh_for_large_db_op); if (load_static_data() == Failure) { set_large_db_notification_off(); goto unlock_quit; } set_large_db_notification_off(); /* ** Before entering the account list processing loop, check to ** see if a STOP command has been issued against the ** process; if not, set the peformance timer to measure the ** timer to calculate the bill for each account on the list. */ if (logout_requested() == ARB_STATUS_LOGOUT) goto unlock_quit; /* ** LOOP through the account list and estimate charge for ** each account (or otherwise process the account). */ while (!loop) { if (logout_requested() == ARB_STATUS_LOGOUT) goto unlock_quit; Invoice_status = Success; trname[0] = '\0'; Dboutput_transaction[0] = '\0'; if (get_next_account(Timer_on) == Failure) { /* * Normal break out of the loop is done here. */ break; } clear_account_info(); free_estimated_charges(); #ifdef MODULE_RCE /* clean Rce_contract_list * if necessary */ if (Rce_contract_list) free_rce_contract_list(); #endif Balance_ref_no = 0; Balance_ref_resets = 0; if (!In_transaction) In_transaction = TRUE; /* ** Calculate the bill for the account. Make Invoice_status ** and be non-resettable to Success after a ** previous failure. */ proc_work_resume(proclogmode,PROCLOG_BILLACC_KEY,NULL,PROCLOG_BILLACC_CV); status =rce_bill_account(); proc_work_incr(proclogmode,PROCLOG_BILLACC_KEY,NULL,PROCLOG_BILLACC_CV,0,0,0,0); proc_work_suspend(proclogmode,PROCLOG_BILLACC_KEY,NULL,PROCLOG_BILLACC_CV); if ((status == Success &&Invoice_status == Success) &&Estimated_charges) if (send_ths_message(dbupdate) != Success) Invoice_status=Failure; if (status != Success && Invoice_status == Success) Invoice_status = status; /* Commit and report each account * separately. */ commit_and_report(trname); /* This is the end of all accounts */ if (logout_requested() == ARB_STATUS_LOGOUT) { goto unlock_quit; } } } /* Finish the process logging */ if (process_log_initialized) { proc_work_end(proclogmode,PROCLOG_PROCACC_KEY,NULL,PROCLOG_PROCACC_CV,Total_accounts,Total_successes+Total_in_error,Total_successes,Total_in_error,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_BILLACC_KEY,NULL,PROCLOG_BILLACC_CV,0,0,0,0,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_BILLDATA_KEY,NULL,PROCLOG_BILLDATA_CV,0,0,0,0,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_EQUPCHRG_KEY,NULL,PROCLOG_EQUPCHRG_CV,0,0,0,0,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_RCS_KEY,NULL,PROCLOG_RCS_CV,0,0,0,0,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_NRCS_KEY,NULL,PROCLOG_NRCS_CV,0,0,0,0,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_BILLDATA_KEY,PROCLOG_GETEQUIP_KEY,PROCLOG_GETEQUIP_CV,0,0,0,0,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_BILLDATA_KEY,PROCLOG_RCDISC_KEY,PROCLOG_RCDISC_CV,0,0,0,0,PROCLOG_SUCCESS); } Proc_run_status = PROCLOG_SUCCESS; bNormalExit = TRUE; /* ** At this point, all accounts on the task list have been processed, * or we've bailed out due to an error. ** Log the results in various places: ** -Timing report to screen and activity log ** -Error/Success summary to activity log ** -Control report */ if (bip_per_task_log() != Success) { break; } /* ** Update the task status as completed in PROCESS_SCHED table. ** Free the memory associated with the accout list. */ if (wrapup_task(dbfetch) == Failure) { emit(BIPMOD, BIP_TASK_UPDATE_ERR); break; } clear_accounts(); /* * Shut down THS_Client */ if (bLoggedIntoTHS) Shutdown_SendToTHS (); if(message)free(message); } unlock_quit: /* ** At this point, the main process loop has been exited. Return ** the process state. */ /* If proc log timers still active then we have exited the loop above in error */ if (bNormalExit == FALSE ) { if (process_log_initialized) { proc_work_end(proclogmode,PROCLOG_PROCACC_KEY,NULL,PROCLOG_PROCACC_CV,Total_accounts,Total_successes+Total_in_error,Total_successes,Total_in_error,PROCLOG_SUCCESS); proc_work_incr_end(proclogmode,PROCLOG_BILLACC_KEY,NULL,PROCLOG_BILLACC_CV,0,0,0,0,PROCLOG_ERROR); proc_work_incr_end(proclogmode,PROCLOG_BILLDATA_KEY,NULL,PROCLOG_BILLDATA_CV,0,0,0,0,PROCLOG_ERROR); proc_work_incr_end(proclogmode,PROCLOG_EQUPCHRG_KEY,NULL,PROCLOG_EQUPCHRG_CV,0,0,0,0,PROCLOG_ERROR); proc_work_incr_end(proclogmode,PROCLOG_RCS_KEY,NULL,PROCLOG_RCS_CV,0,0,0,0,PROCLOG_ERROR); proc_work_incr_end(proclogmode,PROCLOG_NRCS_KEY,NULL,PROCLOG_NRCS_CV,0,0,0,0,PROCLOG_ERROR); proc_work_incr_end(proclogmode,PROCLOG_BILLDATA_KEY,PROCLOG_GETEQUIP_KEY,PROCLOG_GETEQUIP_CV,0,0,0,0,PROCLOG_ERROR); proc_work_incr_end(proclogmode,PROCLOG_BILLDATA_KEY,PROCLOG_RCDISC_KEY,PROCLOG_RCDISC_CV,0,0,0,0,PROCLOG_ERROR); } Proc_run_status = PROCLOG_ERROR; } arb_put_state(dbfetch, ARB_STATUS_LOGOUT); }
/** * A thread that actually handles the unregistration request of the GUI * (internal use only). See rm_unregister_account() for details. * @param args the account ID is stored here * @return empty */ void *unregistration_thread(void *args) { int accountId; // current account int pos; // position of account status information int rc; // return code int timeoutCtr; // timeout counter for sipstack events // accountId is given: accountId = (int) args; LOG_DEBUG(REGISTRAR_MGR_MSG_PREFIX "unregistration thread entered, " "accountId: %d", accountId); // try to gain lock because we want exclusive responsibility for // unregistering this account - also we want to write data: rc = pthread_mutex_lock(&accInfoLock); if (rc != 0) { // failed to gain lock, exit (fatal error) LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "mutex lock could not be" "acquired, error: %d", rc); thread_terminated(); return NULL; } LOG_DEBUG(REGISTRAR_MGR_MSG_PREFIX "mutex lock acquired"); // a registration/update thread should be active and have saved account // information - find position in array of it: rc = find_acc_by_id(accountId, &pos); if (!rc) { // no registration/update thread active LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "account not in use"); rc = go_show_user_event(accountId, "ERROR", "Error unregistering account", "Account is not in use.", "This account is already unregistered."); if (!rc) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "failed to inform the GUI"); } // unlock and exit: pthread_mutex_unlock(&accInfoLock); thread_terminated(); return NULL; } // prevent race conditions because we have to release the lock after // setting doShutdown to 1: if (accInfos[pos].doShutdown || accInfos[pos].isShutdown) { // a second unregister thread was started just when we are trying to // shutdown the active registration thread LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "another thread is already " "unregistering"); rc = go_show_user_event(accountId, "ERROR", "Error unregistering account", "Already unregistering.", "It is currently tried to unregister this " "account."); if (!rc) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "failed to inform the GUI"); } // unlock and exit: pthread_mutex_unlock(&accInfoLock); thread_terminated(); return NULL; } LOG_DEBUG(REGISTRAR_MGR_MSG_PREFIX "account is in use"); // shutdown registration/update thread: accInfos[pos].doShutdown = 1; // release lock to enable abnormal termination (otherwise a dead lock // might occur if registration thread is trying to shutdown in case of // an error while updating): rc = pthread_mutex_unlock(&accInfoLock); if (rc != 0) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "mutex lock could not be " "released, error %d", rc); thread_terminated(); return NULL; } LOG_DEBUG(REGISTRAR_MGR_MSG_PREFIX "wait for registration thread " "to finish"); // now wait on registration thread: // isShutdown == 1 means regular shutdown // accountId == -1 means the account status info was cleared because // of an error while (!accInfos[pos].isShutdown && accInfos[pos].accountId != -1) { sched_yield(); usleep(100000); // 0.1 seconds } LOG_DEBUG(REGISTRAR_MGR_MSG_PREFIX "done waiting"); // we should not send an unregister if account is not registered if (!accInfos[pos].isRegistered || accInfos[pos].accountId == -1) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "unregister failed: account " "is not registered"); rc = go_show_user_event(accountId, "ERROR", "Error unregistering account", "Account is not in use.", "This account is already unregistered."); if (!rc) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "failed to inform the GUI"); } // exit: thread_terminated(); return NULL; } // tell dispatcher which OK is expected: accInfos[pos].eventArrived = 0; accInfos[pos].waitingOnUnregOK = 1; // send REGISTER with expire=0 to registrar: rc = sipstack_send_unregister(accInfos[pos].regId); if (!rc) { // sending REGISTER failed LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "send unregister failed"); rc = go_show_user_event(accountId, "ERROR", "Error unregistering account", "Sending unregistration message failed.", "Failed to send your unregistration " "request to the given registrar." "Please check whether your account " "data is correct and whether your " "internet connection is working " "correctly."); if (!rc) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "failed to inform the GUI"); } // exit: thread_terminated(); return NULL; } // now wait on response: timeoutCtr = 0; while (!accInfos[pos].eventArrived) { sched_yield(); usleep(100000); // 0.1 seconds timeoutCtr++; if (timeoutCtr == config.core.sipOutput.registrarManager.timeout * 10) { break; } } LOG_DEBUG(REGISTRAR_MGR_MSG_PREFIX "done waiting"); // clear flags: accInfos[pos].eventArrived = 0; accInfos[pos].waitingOnUnregOK = 0; // test if account is really unregistered: if (accInfos[pos].isRegistered) { // account is still in use: LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "unregister failed: account " "is still registered"); if (accInfos[pos].informGui) { rc = go_show_user_event(accountId, "ERROR", "Error unregistering account", "Sending unregistration message failed.", "Failed to send your unregistration " "request to the given registrar." "Please check whether your account " "data is correct and whether your " "internet connection is working " "correctly."); if (!rc) { LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "failed to inform the GUI"); } } // don't terminate because there is no point in believing the account // was still registered with the registrar } if (accInfos[pos].informGui) { // inform GUI that unregister succeeded: rc = go_change_reg_status(accountId, 0); if (!rc) { // failed to contact GUI LOG_ERROR(REGISTRAR_MGR_MSG_PREFIX "GUI registration status " "update failed"); // exit: thread_terminated(); return NULL; } } // mark account as no longer used: clear_account_info(pos); LOG_INFO(REGISTRAR_MGR_MSG_PREFIX "unregister of account %d succeeded", accountId); // we are done: thread_terminated(); return NULL; }