Logo Search packages:      
Sourcecode: netams version File versions  Download package

s_billing.c

/*************************************************************************
***   Authentication, authorization, accounting + firewalling package
***   Copyright 1998-2002 Anton Vinokurov <anton@netams.com>
***   Copyright 2002-2008 NeTAMS Development Team
***   This code is GPL v3
***   For latest version and more info, visit this project web page
***   located at http://www.netams.com
***
*************************************************************************/
/* $Id: s_billing.c,v 1.154 2009-08-01 09:23:55 anton Exp $ */

#include "netams.h"

static int initialized=0;

const char *buildforstring=NULL;
PlansList *bPlans=NULL;
SubPlansList *bSubPlans=NULL;
AccountsList *bAccounts=NULL;
Service *Billing=NULL;
extern FeeCounters FC;
//////////////////////////////////////////////////////////////////////////////////////////
void sBiSendAlert(NetUnit *u, u_char dir);
void FillAccountData(void *res, void *row, char* (*getRowData)(void*, void* , u_char));
void FillBillingData(void *res, void *row, char* (*getRowData)(void*, void* , u_char));
int cShowBillingAccount (struct cli_def *cli, const char *cmd, char **argv, int argc);
int cShowBillingPlan    (struct cli_def *cli, const char *cmd, char **argv, int argc);
//////////////////////////////////////////////////////////////////////////////////////////
//defined commands
static const  struct CMD_DB cmd_db[] = {
{ 2, 0, 0, "show",      PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL,        "shows various system parameters" },
{ 0, 2, 0, "account",   PRIVILEGE_UNPRIVILEGED, MODE_EXEC, cShowBillingAccount, "accounts information" },  
{ 0, 2, 0, "plan",      PRIVILEGE_UNPRIVILEGED, MODE_EXEC, cShowBillingPlan,  "billing plans" },
{ 0, 0, 1, "subplan",   PRIVILEGE_UNPRIVILEGED, MODE_BILLING, cServiceProcessCfg,   NULL },
{ 0, 0, 1, "plan",      PRIVILEGE_UNPRIVILEGED, MODE_BILLING, cServiceProcessCfg,   NULL },
{ 0, 0, 1, "account",   PRIVILEGE_UNPRIVILEGED, MODE_BILLING, cServiceProcessCfg,   NULL },
{ 0, 0, 0, "default-credit-limit", PRIVILEGE_UNPRIVILEGED, MODE_BILLING, cServiceProcessCfg, NULL },
{ 0, 0, 0, "start",     PRIVILEGE_UNPRIVILEGED, MODE_BILLING, cServiceStart,            NULL },
{ 0, 0, 0, "stop",      PRIVILEGE_UNPRIVILEGED, MODE_BILLING, cServiceStop,             NULL },
{ -1, 0, 0, NULL,  0, 0, NULL, NULL }
};
//////////////////////////////////////////////////////////////////////////////////////////
class Service_Billing: public Service_Billing_Interface {
public:
      u_char storage;
      Service *st;
      FIFO *fifo;
      pthread_rwlock_t rwlock; // for multiple threads access same config data
      char *bfilename;
      char *sfilename;
      double default_credit_limit;

      Service_Billing();
      ~Service_Billing();

      void ShowCfg(struct cli_def *cli, u_char flags);
      int ProcessCfg(struct cli_def *cli, char **argv, int argc, u_char no_flag);
      void Worker();
      void Cancel();

      void AccountData();
      void sBiCheckDb(int id);
      void BiSyncDb();
};

Service* InitBillingService() {
      Service *s;
      if(!initialized) {
            InitCliCommands(cmd_db);
            initialized = 1;
      }
      initialized++;
      s = (Service*)new Service_Billing();
      s->serv_flags|=SERVICE_FLAG_SINGLE;
      return s;
}
//////////////////////////////////////////////////////////////////////////////////////////
Service_Billing::Service_Billing (): Service_Billing_Interface() {
      //init Plans, SubPlans and Accounts data
      bPlans = new PlansList();
      bSubPlans = new SubPlansList();
      bAccounts = new AccountsList();
      
      st=NULL;
      storage=0; 
      default_credit_limit=0;
      bfilename=NULL;
      sfilename=NULL;
      netams_rwlock_init(&rwlock, NULL);
      fifo = new FIFO(MAX_UNITS);
      ((Service_Processor*)Processor)->fifo=fifo;
      
      print_to_string(&bfilename, "billing.%u", instance);
      print_to_string(&sfilename, "bdata.%u", instance);
      
      Billing=this;
}

Service_Billing::~Service_Billing () {
      netams_rwlock_destroy(&rwlock);
      
      delete fifo;
      
      aFree(bfilename);
      aFree(sfilename);

      //delete Plans, SubPlans and Accounts data
        delete bPlans;          bPlans = NULL;
      delete bSubPlans;       bSubPlans = NULL;
      delete bAccounts;       bAccounts  = NULL;
}
//////////////////////////////////////////////////////////////////////////////////////////
int Service_Billing::ProcessCfg(struct cli_def *cli, char **param, int argc, u_char no_flag){
      
      time_t now=FC.now;
      
      netams_rwlock_wrlock(&rwlock);

      if (STRARG(param[0], "subplan")) {
            SubPlan *sp;

            u_char sp_id=strtol(param[1], NULL, 10);
            if (!sp_id) { goto END; }

            if ((sp=(SubPlan*)bSubPlans->getById(sp_id))==NULL) { 
                  sp=new SubPlan(sp_id);
                  bSubPlans->Insert(sp);
                  cli_error(cli, "subplan %u created", sp_id);
            } else if (no_flag) {
                  if(sp->connected_plans) {
                        cli_error(cli, "subplan connected to %u plans", sp->connected_plans);
                  } else {
                        bSubPlans->Delete(sp);
                        delete sp;
                        cli_error(cli, "subplan %u deleted", sp_id);
                  }
                  goto END;
            }
      
            if (STRARG(param[2], "fee")) {
                  sp->fee=(float)strtod(param[3], NULL);
                  cli_error(cli, "subplan %u periodic fee set: %f", sp->id, sp->fee);
                  if (STREQ(param[4], "spread")) {
                        if (STREQ(param[5], "monthly")) {
                              sp->spread='M';
                              cli_error(cli, "subplan %u spread monthly", sp->id);
                        } else if (STREQ(param[5], "daily")) {
                              sp->spread='D';
                              cli_error(cli, "subplan %u spread daily", sp->id);
                        } else if (STREQ(param[5], "hourly")) {
                              sp->spread='H';
                              cli_error(cli, "subplan %u spread hourly", sp->id);
                        } else
                              cli_error(cli, "subplan %u spread specification invalid", sp->id);
                  }
                  else 
                        cli_error(cli, "subplan %u fee command invalid", sp->id);
            } // fee
            else if (STRARG(param[2], "included")) {
                  sp->flags=SPLAN_NONE;
                  if (STREQ(param[4], "sum")) {
                        sp->flags=SPLAN_SUM;
                        if (STREQ(param[3], "unlimited")) {
                              sp->flags|=SPLAN_UNLIM_SUM;
                              sp->inc_in=sp->inc_out=0;
                        } else {
                              sp->inc_in=sp->inc_out=bytesT2Q(param[3]);
                        }
                        cli_error(cli, "subplan %u included %llu sum", sp_id, sp->inc_in);
                        cli_error(cli, "unlimited %s", sp->flags&SPLAN_UNLIM_SUM?"sum":"none");
                  } else {
                        if (STREQ(param[4], "in")) {
                              if (STREQ(param[3], "unlimited")) {
                                    sp->flags|=SPLAN_UNLIM_IN;
                                    sp->inc_in=0;
                              } else {
                                    sp->inc_in=bytesT2Q(param[3]);
                              }
                        }
                        if (STREQ(param[6], "out")) {
                              if (STREQ(param[5], "unlimited")) {
                                    sp->flags|=SPLAN_UNLIM_OUT;
                                    sp->inc_out=0;
                              } else {
                                    sp->inc_out=bytesT2Q(param[5]);
                              }
                        }
                        cli_error(cli, "subplan %u included %llu in, %llu out",
                              sp_id, sp->inc_in, sp->inc_out);
                        cli_error(cli, "unlimited %s, %s",
                              sp->flags&SPLAN_UNLIM_IN?"in":"none" ,
                              sp->flags&SPLAN_UNLIM_OUT?"out":"none");
                  }
            } // included
            else if (STRARG(param[2], "adjust-included")) {
                  unsigned adj=0;
                  if (STREQ(param[3], "no")) adj=0;
                  else if (STREQ(param[3], "yes")) adj=1;
                  cli_error(cli, "subplan %u adjust included: %s", sp_id, adj?"yes":"no");
                  sp->inc_adjust=adj;
            } // adjust-included
            else if (STRARG(param[2], "adjust-fee")) {
                  unsigned adj=1;
                  if (STREQ(param[3], "no")) adj=0;
                  else if (STREQ(param[3], "yes")) adj=1;
                  cli_error(cli, "subplan %u adjust fee: %s", sp_id, adj?"yes":"no");
                  sp->fee_adjust=adj;
            } // adjust-included
            else if (STRARG(param[2], "policy")) {
                  Policy *p;
                  policy_flag flags=POLICY_FLAG_NONE;
                  char *c_param=param[3];

                  if (c_param[0]=='!') { flags|=POLICY_FLAG_INV; c_param++; }
                  if (c_param[0]=='%') { flags|=POLICY_FLAG_BRK; c_param++; }
                  if (c_param[0]=='!') { flags|=POLICY_FLAG_INV; c_param++; }

                  if ((p=PolicyL->getPolicy(c_param))) {
                        sp->pid=p->id;
                        //clear pervious policy flags
                        sp->policy_flags&=~(POLICY_FLAG_INV|POLICY_FLAG_BRK); 
                        //set new ones
                        sp->policy_flags|=flags;
                        cli_error(cli, "subplan %u policy set to %s%s%s", 
                              sp->id,
                              (sp->policy_flags&POLICY_FLAG_INV)?"!":"",
                              (sp->policy_flags&POLICY_FLAG_BRK)?"%":"", c_param);
                  }
                  else
                        cli_error(cli, "subplan %u policy unknown", sp->id);
            } // policy
            else if (STRARG(param[2], "overdraft")) {
                  if (STREQ(param[4], "sum")) {
                        sp->pay_in=sp->pay_in=(float)strtod(param[3], NULL);
                        sp->overdraft_in=sp->overdraft_out=(double)(sp->pay_in)/MEGABYTE;
                        cli_error(cli, "subplan %u overdraft %f sum", sp->id, sp->pay_in);
                  } else {
                        if (STREQ(param[4], "in")) {
                              sp->pay_in=(float)strtod(param[3], NULL);
                              sp->overdraft_in=(double)(sp->pay_in)/MEGABYTE;
                        }
                        if (STREQ(param[6], "out")) {
                              sp->pay_out=(float)strtod(param[5], NULL);
                              sp->overdraft_out=(double)(sp->pay_out)/MEGABYTE;
                        }
                        cli_error(cli, "subplan %u overdraft %f in, %f out", sp->id, sp->pay_in, sp->pay_out);
                  }
            } // overdraft
            else cli_error(cli, "subplan %u command invalid", sp->id);
      } // subplan
      else if (STRARG(param[0], "plan")) {
            Plan *pl;
            int k=2;
            
            u_char pl_id=strtol(param[1], NULL, 10);
            if ((pl=(Plan*)bPlans->getById(pl_id))==NULL) { 
                  pl=new Plan(pl_id);
                  bPlans->Insert(pl);
                  cli_error(cli, "plan %06X created", pl_id);
            }  else if (no_flag) {
                  if(pl->connected_accounts) {
                        cli_error(cli, "plan connected to %u accounts", pl->connected_accounts);
                  } else {
                        bPlans->Delete(pl);
                        delete pl;
                        cli_error(cli, "plan %u deleted", pl_id);
                  }
                  goto END;
            }

            if (STRARG(param[2], "name")) {
                  if (pl->name) aFree(pl->name);
                  pl->name=set_string(param[3]);
                  cli_error(cli, "plan %u name set to %s", pl_id, pl->name);
            } //name
            else if (STRARG(param[2], "description")) {
                  if (pl->description) aFree(pl->description);
                  pl->description=set_string(param[3]);
                  cli_error(cli, "plan %u description set to \"%s\"", pl_id, pl->description);
            } //description
            else if (STREQ(param[2], "no")) {
                  k++;
            }
            if (STRARG(param[k], "subplan")) {
                  u_char i=k+1, j=0; SubPlan *s;
                  while (param[i] && (j=strtol(param[i], NULL, 10))) { 
                        if ((s=(SubPlan*)bSubPlans->getById(j))) {
                              if (k==2) {
                                    if(pl->AddSubPlan(s,ADD))
                                          cli_error(cli, "plan %u subplan %u added", pl_id, s->id);
                              } else /* k==3, or 'no' */ {
                                    if(pl->AddSubPlan(s,REMOVE))
                                          cli_error(cli, "plan %u subplan %u removed", pl_id, s->id);
                              }
                        }
                        else cli_error(cli, "plan %u subplan %u not exist\n", pl_id, j);
                        i++;
                  }
            } //subplan
            else cli_error(cli, "plan %u command invalid", pl->id);
      } // plan
      else if (STRARG(param[0], "account")) {
            Account  *ac;

            ac=bAccounts->Get(param[1]);
            if (ac==NULL) {
                  if(no_flag) goto END;
                  ac=new Account();
                  ac->id=newOid();
                  ac->credit_limit=default_credit_limit;
                  bAccounts->Insert(ac);
                  cli_error(cli, "account %06X created", ac->id);
            } else {
                  if(no_flag) {
                        cli_error(cli, "account %06X deleted", ac->id);
                        ac->status|=ACCOUNT_DELETED;
                        ac->created=0;  //this means deleted
                        ac->status|=ACCOUNT_NEED_SYNC;
                  }
            }
            ac->changed=now;

            if (STRARG(param[2], "name")) {
                  ac->setName(param[3]);        
                  ac->status|=ACCOUNT_NEED_SYNC;
                  cli_error(cli, "account %06X name set to %s", ac->id, ac->name);
            } //name
            else if (STRARG(param[2], "description")) {
                  if (ac->description) aFree(ac->description);
                  ac->description=set_string(param[3]);
                  ac->status|=ACCOUNT_NEED_SYNC;
                  cli_error(cli, "account %06X description set to %s", ac->id, ac->description);
            } //description
            else if (STRARG(param[2], "email")) {
                  if (ac->email) aFree(ac->email);
                  ac->email=set_string(param[3]);
                  ac->status|=ACCOUNT_NEED_SYNC;
                  cli_error(cli, "account %06X email set to %s", ac->id, ac->email);
            } //email
            else if (STRARG(param[2], "password")) {
                  if (ac->password) aFree(ac->password);
                  ac->password=set_string(param[3]);
                  ac->status|=ACCOUNT_NEED_SYNC;
                  cli_error(cli, "account %06X password set to %s", ac->id, ac->password);
            } //password 
            else if (STRARG(param[2], "plan")) {
                  Plan *pl=(Plan*)bPlans->getById(strtol(param[3], NULL, 10));
                  if (pl) {
                        ac->Update(pl);
                        cli_error(cli, "account %06X plan set to %u %s",
                              ac->id, pl->id, pl->name?pl->name:"<\?\?>");
                        ac->Balance(0, BAL_ADD); //not ChargeFee() due some checks
                  }
                  else
                        cli_error(cli, "account %06X no plan %s exist", ac->id, param[3]);
            } //plan 
            else if (STRARG(param[2], "nextplan")) {
                  Plan *pl=(Plan*)bPlans->getById(strtol(param[3], NULL, 10));
                  if (pl)     {
                        ac->nextplan=pl;
                        ac->status|=ACCOUNT_NEED_SYNC;
                        ac->nextplan_ch=now;
                        cli_error(cli, "account %06X next plan set to %u %s",
                              ac->id, pl->id, pl->name?pl->name:"<\?\?>");
                  } else
                        cli_error(cli, "account %06X no plan %s exist", ac->id, param[3]);
            } // next plan 
            else if (STREQ(param[2], "beblock")) {
                  ac->UpdateStatus(AC_BEBLOCK, now);
                  cli_error(cli, "account %06X block pending", ac->id);
            }
            else if (STREQ(param[2], "block")) {
                  ac->UpdateStatus(AC_BLOCK, now);
                  cli_error(cli, "account %06X blocked", ac->id);
            } //block 
            else if (STREQ(param[2], "unblock")) {
                  ac->UpdateStatus(AC_UNBLOCK, now);
                  cli_error(cli, "account %06X unblocked", ac->id);
                  ac->Balance(0, BAL_ADD); //not ChargeFee() due some checks
            } //unblock 
            else if (STREQ(param[2], "balance")) {
                  double delta=0;
                  const char *act;
                  
                  if(param[4]) delta=(double)strtod(param[4], NULL);

                  if (STRARG(param[3], "add")) {
                        ac->Balance(delta, BAL_ADD);
                        act="ADD";
                  }
                  else if (STRARG(param[3], "remove")) {
                        ac->Balance(delta, BAL_REMOVE);
                        act="REMOVE";
                  }
                  else if (STRARG(param[3], "set")) {
                        ac->Balance(delta, BAL_SET);
                        act="SET";
                  } else {
                        cli_error(cli, "wrong balance command: %s", param[3]);
                        goto END;
                  }
                  
                  char action[64];
                  sprintf(action,"BALANCE %s %lf ",act, delta);

                  LogEvent(BILLING, 0, 0, ac->id, "balance %s %lf now %lf", act, delta, ac->balance);
                  cli_error(cli, "account %s (%06X) balance %s = %lf, now %lf",
                        ac->name, ac->id, param[3], delta, ac->balance);
            } //balance 
            else if (STRARG(param[2], "credit-limit")) {
                  double lim=strtod(param[3], NULL);

                  if (lim<=0) {
                        ac->credit_limit=lim;
                        ac->Balance(0, BAL_ADD); // maybe we can unblock it now?
                        LogEvent(BILLING, 0, 0, ac->id, "credit_limit for %s is %lf", ac->name, ac->credit_limit);
                        cli_error(cli, "account %s (%06X) credit limit %lf",
                              ac->name, ac->id,  ac->credit_limit);
                  }
                  else 
                        cli_error(cli, "wrong credit limit specified for %s: must be nonpositive", ac->name);

            } //credit-limit 
            else if (STRARG(param[2], "unit")) {
                  NetUnit *u=NULL;
                  u_char i=3;
                  
                  u=aParseUnit(param, &i);
                  if (!u) {
                        cli_error(cli, "Unknown unit");
                        goto END;
                  }
                  if (STREQ(param[i], "add")) {
                        if(!u->account) {
                              if(ac->AddUnit(u, ADD))
                                    cli_error(cli, "account %06X add unit %06X (%s)",
                                          ac->id, u->id, u->name?u->name:"<\?\?>");
                        } else {
                              cli_error(cli, "Unit %s(%06X) already belongs to account %s(%06X)",
                                    u->name, u->id, ac->name, ac->id);
                        }
                  } // add
                  else if (STREQ(param[i], "delete")){
                        if(ac->AddUnit(u, REMOVE)) 
                              cli_error(cli, "account %06X delete unit %06X (%s)",
                                    ac->id, u->id, u->name?u->name:"<\?\?>");
                  } //delete
            } //unit

            if(ac->status&ACCOUNT_NEED_SYNC) Wakeup();

      } // account              
      else if (STRARG(param[0], "default-credit-limit")) {
            double lim=strtod(param[1], NULL);

            if (lim<=0) {
                  default_credit_limit=lim;
                  cli_error(cli, "default credit limit %lf (for next accounts!)", default_credit_limit);
            }
            else
                  cli_error(cli, "wrong default credit limit specified: must be nonpositive");
      } //default-credit-limit 
END:  
      netams_rwlock_unlock(&rwlock);
      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////
void Service_Billing::ShowCfg(struct cli_def *cli, u_char flags){ 
      char b1[32], b2[32];
      SubPlan *sp;
      Policy *p;
      
    if(netams_rwlock_rdlock(&rwlock)) return;

      netams_rwlock_rdlock(&bSubPlans->rwlock);
      for (sp=(SubPlan*)bSubPlans->root; sp!=NULL; sp=(SubPlan*)sp->next){
            if (sp->spread=='M') cli_print(cli, "subplan %u fee %f spread monthly", sp->id, sp->fee);
            if (sp->spread=='D') cli_print(cli, "subplan %u fee %f spread daily", sp->id, sp->fee);
            if (sp->spread=='H') cli_print(cli, "subplan %u fee %f spread hourly", sp->id, sp->fee);
            if (sp->flags&SPLAN_SUM)
                  cli_print(cli, "subplan %u included %s sum\n",
                        sp->id, sp->flags&SPLAN_UNLIM_SUM?"unlimited":bytesQ2T(sp->inc_in, b1));
            else
                  cli_print(cli, "subplan %u included %s in %s out",
                        sp->id, sp->flags&SPLAN_UNLIM_IN?"unlimited":bytesQ2T(sp->inc_in, b1),
                        sp->flags&SPLAN_UNLIM_OUT?"unlimited":bytesQ2T(sp->inc_out, b2));
            if (sp->inc_adjust) cli_print(cli, "subplan %u adjust-included yes", sp->id);
            cli_print(cli, "subplan %u adjust-fee %s", sp->id, sp->fee_adjust?"yes":"no");
            if((p=(Policy*)PolicyL->getById(sp->pid)))
                  cli_print(cli, "subplan %u policy %s%s%s", sp->id,
                        (sp->policy_flags&POLICY_FLAG_INV)?"!":"",
                        (sp->policy_flags&POLICY_FLAG_BRK)?"%":"", p->name);
            if(sp->flags&SPLAN_SUM)
                  cli_print(cli, "subplan %u overdraft %f sum",  sp->id, sp->pay_in);
            else 
                  cli_print(cli, "subplan %u overdraft %f in %f out", sp->id, sp->pay_in, sp->pay_out);
      }
      netams_rwlock_unlock(&bSubPlans->rwlock);

      Plan *pl;
      netams_rwlock_rdlock(&bPlans->rwlock);
      for (pl=(Plan*)bPlans->root; pl!=NULL; pl=(Plan*)pl->next){
            cli_print(cli, "plan %u name %s", pl->id, pl->name?pl->name:"<\?\?>");
            cli_print(cli, "plan %u description \"%s\"", pl->id, pl->description?pl->description:"<\?\?>");
            if (pl->root) {
                  cli_bufprint(cli, "plan %u subplan", pl->id);
                  for (bSPlist *spl=pl->root; spl!=NULL; spl=spl->next)
                        cli_bufprint(cli, " %u", spl->sp->id); 
                  cli_bufprint(cli, "\n");
            }
      }
      netams_rwlock_unlock(&bPlans->rwlock);

      if (default_credit_limit<0)
            cli_print(cli, "default-credit-limit %f", default_credit_limit);
      
      netams_rwlock_unlock(&rwlock);
}
//////////////////////////////////////////////////////////////////////////////////////////
void Service_Billing::Worker(){
      st = aStorageGetAccepted(ST_CONN_BILLING);
      if (st==NULL) {
            aLog(D_WARN, "billing service requires at least one storage to be up, skipping\n");
            return;
    }
      
      // get all accounts information from storage into RAM (bAccounts list)
      while(!(((Service_Storage_Interface*)st)->stLoad(ST_CONN_BILLING, &FillAccountData))) {
            aLog(D_WARN, "Service billing can't obtain accounts from storage:%u\n", st->instance);
            aLog(D_WARN, "Service PROCESSOR will be BLOCKED until read completed!\n");
            Sleep(10);
      }
      Processor->Wakeup();    //read units data, Processor blocked until this

      bAccounts->RestoreAccounts(); //restore back Account status
      
      // get all accounts bdata information from storage into RAM (billing data)
      while(!(((Service_Storage_Interface*)st)->stLoad(ST_CONN_BDATA, &FillBillingData))) {
            aLog(D_WARN, "Service billing can't obtain bdata from storage:%u\n", st->instance);
            Sleep(10);
      }

      aLog(D_DEBUG, "checking every every processor delay: %d seconds\n", Processor->delay);

      while (1) {
            Sleep(0);
            AccountData();
            
      }
}
//////////////////////////////////////////////////////////////////////////////////////////
void Service_Billing::Cancel(){
      AccountData();
      
      if(st) {
            ((Service_Storage_Interface*)st)->Close(ST_CONN_BILLING);
            ((Service_Storage_Interface*)st)->Close(ST_CONN_BDATA);
      }

      if(Processor) Processor->fifo=NULL;
      Billing = NULL;
}
//////////////////////////////////////////////////////////////////////////////////////////
int cShowBillingAccount(struct cli_def *cli, const char *cmd, char **argv, int argc){
      
      Service *s=Billing;
      if(!s) {
            cli_print(cli, "Service not enabled");
            return CLI_OK;
      }

      u_char i=2;
      Account *ac, *acx=NULL;
      u_char isfull=0;
      u_char bdata=0;
      const char *blocked;

      if (argc>2) acx=(Account*)bAccounts->Get(argv[i]);

      if (STREQ(argv[i+1], "list")) {
            netams_rwlock_rdlock(&bAccounts->rwlock);
            for (ac=(Account*)bAccounts->root; ac!=NULL; ac=(Account*)ac->next) 
                  if (!acx || acx==ac) 
                        cli_print(cli, "%s %06X ", ac->name, ac->id);
            netams_rwlock_unlock(&bAccounts->rwlock);
            return CLI_OK;
      }
      else if (STREQ(argv[i+1], "full")) isfull=1;
      
      if (STREQ(argv[i], "bdata") || STREQ(argv[i+1], "bdata")) bdata=1;

      netams_rwlock_rdlock(&bAccounts->rwlock);
      for (ac=(Account*)bAccounts->root; ac!=NULL; ac=(Account*)ac->next){
            if (ac->status&ACCOUNT_DELETED) continue; //this account deleted
            if (!acx || acx==ac) {
                  if(ac->status&ACCOUNT_BLOCKED ) blocked="BLOCKED";
                  else if (ac->status&ACCOUNT_BEBLOCKED) blocked="BEBLOCKED";
                  else blocked="UNBLOCKED";

                  cli_print(cli, "Name %s (%06X) %s %s bal: %lf cred_lim: %.2lf plan: %s",
                        ac->name, ac->id, blocked,
                        ac->status&ACCOUNT_NEED_SYNC?"NOTSYNC":"SYNC",
                        ac->balance, ac->credit_limit,
                        ac->plan?(ac->plan->name?ac->plan->name:"<\?\?>"):"-");
                  if (isfull) {
                        cli_print(cli, "Plan %s %u %lu Nextplan %s %u %lu",
                              ac->plan?(ac->plan->name?ac->plan->name:"<\?\?>"):"-",
                              ac->plan?ac->plan->id:0, (u_long)ac->plan_ch,
                              ac->nextplan?(ac->nextplan->name?ac->nextplan->name:"<\?\?>"):"-",
                              ac->nextplan?ac->nextplan->id:0, (u_long)ac->nextplan_ch);
                        cli_print(cli, "Changed %lu Blocked %lu Created %lu",
                              (u_long)ac->changed, (u_long)ac->blocked, (u_long)ac->created);
                        cli_print(cli,    "Email %s Password %s",
                              ac->email?ac->email:"-",
                              ac->password?ac->password:"-");
                  }

                  char res[256]; bzero(res,256);
                  strcpy(res,"   Units:");
                  for (bUlist *bu=ac->bUroot; bu!=NULL; bu=bu->next) {
                        NetUnit *u=bu->u;
                        sprintf(res+strlen(res)," %s %06X", u->name?u->name:"<\?\?>", u->id);
                  }
                  cli_print(cli, "%s", res);

                  if(bdata && ac->plan) {
                        u_char poz=0;
                        billing_data *bd;
                        cli_print(cli, "Bstats:");
                        cli_print(cli, "Account\tSubPlan\tPrefix\tIn\tOut\tPay_in\t\tPay_out");
                        for(bSPlist *bsp=ac->plan->root;bsp!=NULL; bsp=bsp->next, poz++) {
                              bd=&ac->data[poz];
                              cli_print(cli, "%06X\t%06X\t%c\t%lld\t%lld\t%lf\t%lf",
                                    ac->id,bsp->sp->id,'H',bd->h.in,bd->h.out,bd->h.pay_in,bd->h.pay_out);
                              cli_print(cli, "%06X\t%06X\t%c\t%lld\t%lld\t%lf\t%lf",
                                    ac->id,bsp->sp->id,'D',bd->d.in,bd->d.out,bd->d.pay_in,bd->d.pay_out);
                              cli_print(cli, "%06X\t%06X\t%c\t%lld\t%lld\t%lf\t%lf",
                                    ac->id,bsp->sp->id,'W',bd->w.in,bd->w.out,bd->w.pay_in,bd->w.pay_out);
                              cli_print(cli, "%06X\t%06X\t%c\t%lld\t%lld\t%lf\t%lf",
                                    ac->id,bsp->sp->id,'M',bd->m.in,bd->m.out,bd->m.pay_in,bd->m.pay_out);
                        }
                  }
            } // if
      } // for
      netams_rwlock_unlock(&bAccounts->rwlock);
      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////
int cShowBillingPlan(struct cli_def *cli, const char *cmd, char **argv, int argc){
      Service *s=Billing;
      if(!s) {
            cli_print(cli, "Service not enabled");
            return CLI_OK;
      }

      unsigned pl_id=0, a=0, b=0;
      if (argc>2) pl_id=strtol(argv[2], NULL, 10);
      if (STREQ(argv[3], "accounts")) a=1;
      else if (STREQ(argv[3], "list")) b=1; 
      char b1[32], b2[32];

      Plan *pl; SubPlan *sp;

      netams_rwlock_rdlock(&bPlans->rwlock);
      for (pl=(Plan*)bPlans->root; pl!=NULL; pl=(Plan*)pl->next){
            if (!pl_id || pl_id==pl->id) {
                  cli_print(cli, "Plan ID %u Name \"%s\" Desc. \"%s\"",
                        pl->id, pl->name?pl->name:"",
                        pl->description?pl->description:"");
                  if (a && !b) {
                        Account *ac;
                        netams_rwlock_rdlock(&bAccounts->rwlock);
                        for (ac=(Account*)bAccounts->root; ac!=NULL; ac=(Account*)ac->next){
                              if (ac->plan==pl)
                                    cli_print(cli, " %s", ac->name?ac->name:"<\?\?>");
                        }
                        netams_rwlock_unlock(&bAccounts->rwlock);
                  }
                  else if (!b) {    
                        Policy *p;
                        for (bSPlist *spl=pl->root; spl!=NULL; spl=spl->next) {
                              sp=spl->sp;
                              if (!sp) continue;
                              p=(Policy*)PolicyL->getById(sp->pid);
                              cli_print(cli, "\tSubplan ID %u", sp->id);
                              cli_print(cli, "\t  Fee %f, spread: '%c', policy %s(%06X)",
                                    sp->fee, sp->spread, p?p->name:"\?\?", sp->pid);
                              if(sp->flags&SPLAN_SUM) {
                                    cli_print(cli, "\t  Incl. %s sum Over. %f/M sum",
                                          sp->flags&SPLAN_UNLIM_SUM?"unlimited":bytesQ2T(sp->inc_in, b1),
                                          sp->pay_in);
                              } else {
                                    cli_print(cli, "\t  Incl. %s in %s out, Over. %f/M in %f/M out",
                                          sp->flags&SPLAN_UNLIM_IN?"unlimited":bytesQ2T(sp->inc_in, b1), 
                                          sp->flags&SPLAN_UNLIM_OUT?"unlimited":bytesQ2T(sp->inc_out, b2),
                                          sp->pay_in, sp->pay_out);
                              }
                        }
                  }
            }
      }
      netams_rwlock_unlock(&bPlans->rwlock);
      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////
void Service_Billing::AccountData() {
      Message_Store *msg;
      Account *ac;
      NetUnit *u;

      while((msg=(Message_Store*)fifo->TryPop())) {
            aDebug(DEBUG_BILLING, "P->Billing unit:%06X acct:%06X from:%u to:%u\n",msg->netunit, msg->ap, msg->data->from, msg->ts);
            // here we might assume that messages goes in ordered way
            // so there is an order in policies
            // but it probably later
            if((u=(NetUnit*)Units->getById(msg->netunit)) && (ac=u->account)) ac->AccountMessage(msg);
            fifo->Pop();
      }
      
      //sync data with storage
      BiSyncDb();
      
      bAccounts->UpdateAccounts();
}

//////////////////////////////////////////////////////////////////////////////////////////
void Service_Billing::BiSyncDb() {
      unsigned num_accounts=0;
      unsigned num_bdatas=0;

      //prepare account data file
      FILE *bfile=fopen(bfilename,"a");
      if(!bfile) {
            aLog(D_WARN, "Can't create temporary file %s: %s\n", bfilename, strerror(errno));
            return;
      }
      setlinebuf(bfile);

      //prepare billing data file
      FILE *sfile=fopen(sfilename,"a");
      if(!sfile) {
            aLog(D_WARN, "Can't create temporary file %s: %s\n", sfilename, strerror(errno));
            return;
      }
      setlinebuf(sfile);

      Account *ac=(Account*)bAccounts->root;
      Account *prev=NULL;

      netams_rwlock_wrlock(&bAccounts->rwlock);
      while(ac) {
            //sync account
            if (ac->status&ACCOUNT_NEED_SYNC) {
                  ac->SyncAccount(bfile);
                  num_accounts++;
            }

            //sync bdata
            if(ac->status&ACCOUNT_BDATA_NEED_SYNC) {
                  num_bdatas += ac->SyncBdata(sfile);
            }

            if(ac->status&ACCOUNT_DELETED) {
                  if(ac==bAccounts->root) bAccounts->root=ac->next;
                  else prev->next=ac->next;
                  bAccounts->num_objects--;
                  //free memory
                  Account *tmp=(Account*)ac->next;
                  delete ac;
                  ac=tmp;
            } else {
                  prev=ac;
                  ac=(Account*)ac->next;
            }
      }
      netams_rwlock_unlock(&bAccounts->rwlock);

      if(num_accounts) {
            fclose(bfile);
            aDebug(DEBUG_BILLING, "SQL->HDD/billing %u queries\n", num_accounts);
            ((Service_Storage_Interface*)st)->SaveFile(bfilename,ST_CONN_BILLING);
      } else 
            fclose(bfile);

      if(num_bdatas) {
            fclose(sfile);
            aDebug(DEBUG_BILLING, "SQL->HDD/bdata %u queries\n", num_bdatas);
            ((Service_Storage_Interface*)st)->SaveFile(sfilename,ST_CONN_BDATA);
      } else 
            fclose(sfile);
}
//////////////////////////////////////////////////////////////////////////////////////////
void FillAccountData(void *res, void *row, char* (*getRowData)(void*, void* , u_char)) {
        Account *ac;
        NetUnit *u;
        unsigned t;
        oid id;

        sscanf(getRowData(res, row, 0), "%u", &id);
        newOid(id);

        sscanf(getRowData(res, row, 10), "%u", &t); //created ?
        if(!t) return; //this account deleted

        ac=new Account();
        ac->id=newOid(id);

        if (getRowData(res, row, 1)[0]) ac->name=set_string(getRowData(res, row, 1));
        if (getRowData(res, row, 2)[0]) ac->description=set_string(getRowData(res, row, 2));

        sscanf(getRowData(res, row, 3), "%lf", &ac->balance);

        sscanf(getRowData(res, row, 5), "%u", &t); ac->plan=(Plan*)bPlans->getById(t);
        sscanf(getRowData(res, row, 6), "%lu", (unsigned long*)&ac->plan_ch);
        sscanf(getRowData(res, row, 7), "%u", &t); ac->nextplan=(Plan*)bPlans->getById(t);
        sscanf(getRowData(res, row, 8), "%lu", (unsigned long*)&ac->nextplan_ch);
        sscanf(getRowData(res, row, 9), "%lu", (unsigned long*)&ac->blocked);
        sscanf(getRowData(res, row, 10), "%lu", (unsigned long*)&ac->created);
        sscanf(getRowData(res, row, 11), "%lu", (unsigned long*)&ac->changed);
        sscanf(getRowData(res, row, 12), "%lu", (unsigned long*)&ac->last_fee_ch);

        if (getRowData(res, row, 13)[0]) ac->email=set_string(getRowData(res, row, 13));
        if (getRowData(res, row, 14)[0]) ac->password=set_string(getRowData(res, row, 14));
        sscanf(getRowData(res, row, 15), "%u", &t); ac->status=t;
        if (getRowData(res, row, 16)!=NULL) sscanf(getRowData(res, row, 16), "%lf", &ac->credit_limit);

        u_short i=0,k=strlen(getRowData(res, row, 4));
        while (i*7<k) {
                sscanf(getRowData(res, row, 4)+i*7, "%06X", &t);
                if ((u=(NetUnit*)Units->getById(t)))
                        ac->AddUnit(u, ADD);
                else
                        break;
                i++;
        }
        bAccounts->Insert(ac);
}
//////////////////////////////////////////////////////////////////////////////////////////
void FillBillingData(void *res, void *row, char* (*getRowData)(void*, void* , u_char)) {
        Account *ac;
        bstat *bs=NULL;
        bSPlist *bsp;
        unsigned id;
        time_t t_from;
        char prefix;
        int i;

        sscanf(getRowData(res, row, 0), "%u", &id);     //account_id
        if(!(ac=(Account*)bAccounts->getById(id))) return;
        if(ac->plan==NULL) return;

        sscanf(getRowData(res, row, 1), "%u", &id);     //subplan_id
        sscanf(getRowData(res, row, 2), "%lu", (u_long*)&t_from);
        sscanf(getRowData(res, row, 3), "%c", &prefix);

        for(i=0,bsp=ac->plan->root;bsp!=NULL;bsp=bsp->next, i++) {
                if(bsp->sp->id!=id) continue;

                switch (prefix){
                        case 'M':
                                bs=&ac->data[i].m;
                                break;
                        case 'W':
                                bs=&ac->data[i].w;
                                break;
                        case 'D':
                                bs=&ac->data[i].d;
                                break;
                        case 'H':
                                bs=&ac->data[i].h;;
                                break;
                }
                sscanf(getRowData(res, row, 4), "%lld", &bs->in);
                sscanf(getRowData(res, row, 5), "%lld", &bs->out);
                sscanf(getRowData(res, row, 6), "%lf", &bs->pay_in);
                sscanf(getRowData(res, row, 7), "%lf", &bs->pay_out);

                break;
        }
        aDebug(DEBUG_BILLING, "Account %s(%06X), '%c' bstat loaded\n", ac->name, ac->id, prefix);
}
//////////////////////////////////////////////////////////////////////////////////////////

Generated by  Doxygen 1.6.0   Back to index