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();
}
Exemple #3
0
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;
}