static void do_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { int i; struct Client *acptr; struct Client *acptr2; const struct ConnectionClass* cl; char* tname; int doall; int *link_s; int *link_u; int cnt = 0; int wilds; int dow; if (parc < 2 || BadPtr(parv[1])) { /* just "TRACE" without parameters. Must be from local client */ parc = 1; acptr = &me; tname = cli_name(&me); i = HUNTED_ISME; } else if (parc < 3 || BadPtr(parv[2])) { /* No target specified. Make one before propagating. */ parc = 2; tname = parv[1]; if ((acptr = find_match_server(parv[1])) || ((acptr = FindClient(parv[1])) && !MyUser(acptr))) { if (IsUser(acptr)) parv[2] = cli_name(cli_user(acptr)->server); else parv[2] = cli_name(acptr); parc = 3; parv[3] = 0; if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr), "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; } else i = HUNTED_ISME; } else { /* Got "TRACE <tname> :<target>" */ parc = 3; if (MyUser(sptr) || Protocol(cptr) < 10) acptr = find_match_server(parv[2]); else acptr = FindNServer(parv[2]); if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; tname = parv[1]; } if (i == HUNTED_PASS) { if (!acptr) acptr = next_client(GlobalClientList, tname); else acptr = cli_from(acptr); send_reply(sptr, RPL_TRACELINK, version, debugmode, tname, acptr ? cli_name(cli_from(acptr)) : "<No_match>"); return; } doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1; wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?'); dow = wilds || doall; /* Don't give (long) remote listings to lusers */ if (dow && !MyConnect(sptr) && !IsAnOper(sptr)) { send_reply(sptr, RPL_TRACEEND); return; } link_s = MyCalloc(2 * maxconnections, sizeof(link_s[0])); link_u = link_s + maxconnections; if (doall) { for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (IsUser(acptr)) link_u[cli_fd(cli_from(acptr))]++; else if (IsServer(acptr)) link_s[cli_fd(cli_from(acptr))]++; } } /* report all direct connections */ for (i = 0; i <= HighestFd; i++) { const char *conClass; if (!(acptr = LocalClientArray[i])) /* Local Connection? */ continue; if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) && !IsAnOper(acptr) && (acptr != sptr)) continue; if (!doall && wilds && match(tname, cli_name(acptr))) continue; if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr))) continue; conClass = get_client_class(acptr); switch (cli_status(acptr)) { case STAT_CONNECTING: send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr)); cnt++; break; case STAT_HANDSHAKE: send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr)); cnt++; break; case STAT_ME: break; case STAT_UNKNOWN: case STAT_UNKNOWN_USER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, get_client_name(acptr, HIDE_IP)); cnt++; break; case STAT_UNKNOWN_SERVER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server"); cnt++; break; case STAT_USER: /* Only opers see users if there is a wildcard but anyone can see all the opers. */ if ((IsAnOper(sptr) && (MyUser(sptr) || !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) { if (IsAnOper(acptr)) send_reply(sptr, RPL_TRACEOPERATOR, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); else send_reply(sptr, RPL_TRACEUSER, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); cnt++; } break; /* * Connection is a server * * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age> * * class Class the server is in * nS Number of servers reached via this link * nC Number of clients reached via this link * name Name of the server linked * ConnBy Who established this link * last Seconds since we got something from this link * age Seconds this link has been alive * * Additional comments etc...... -Cym-<*****@*****.**> */ case STAT_SERVER: if (cli_serv(acptr)->user) { if (!cli_serv(acptr)->by[0] || !(acptr2 = findNUser(cli_serv(acptr)->by)) || (cli_user(acptr2) != cli_serv(acptr)->user)) acptr2 = NULL; send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), acptr2 ? cli_name(acptr2) : "*", cli_serv(acptr)->user->username, cli_serv(acptr)->user->host, CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); } else send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*", cli_name(&me), CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); cnt++; break; default: /* We actually shouldn't come here, -msa */ send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP)); cnt++; break; } } /* * Add these lines to summarize the above which can get rather long * and messy when done remotely - Avalon */ if (IsAnOper(sptr) && doall) { for (cl = get_class_list(); cl; cl = cl->next) { if (Links(cl) > 1) send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl) - 1); } } send_reply(sptr, RPL_TRACEEND); MyFree(link_s); }
/** Report memory usage statistics to a client. * @param cptr Client to send data to. * @param sd StatDesc that generated the stats request (ignored). * @param param Extra parameter from user (ignored). */ void count_memory(struct Client *cptr, const struct StatDesc *sd, char *param) { struct Client *acptr; struct Invite *inv; struct SLink *link; struct Ban *ban; struct Channel *chptr; struct ConfItem *aconf; const struct ConnectionClass* cltmp; struct Membership* member; int acc = 0, /* accounts */ c = 0, /* clients */ cn = 0, /* connections */ ch = 0, /* channels */ lcc = 0, /* local client conf links */ chb = 0, /* channel bans */ wwu = 0, /* whowas users */ cl = 0, /* classes */ co = 0, /* conf lines */ listeners = 0, /* listeners */ memberships = 0; /* channel memberships */ int usi = 0, /* users invited */ aw = 0, /* aways set */ wwa = 0, /* whowas aways */ gl = 0, /* glines */ ju = 0; /* jupes */ size_t chm = 0, /* memory used by channels */ chbm = 0, /* memory used by channel bans */ cm = 0, /* memory used by clients */ cnm = 0, /* memory used by connections */ us = 0, /* user structs */ usm = 0, /* memory used by user structs */ awm = 0, /* memory used by aways */ wwam = 0, /* whowas away memory used */ wwm = 0, /* whowas array memory used */ wt = 0, /* watch entrys */ wtm = 0, /* memory used by watchs */ #if defined(DDB) dbs = 0, /* keys of database */ dbm = 0, /* memory used by DDB */ #endif glm = 0, /* memory used by glines */ jum = 0, /* memory used by jupes */ com = 0, /* memory used by conf lines */ dbufs_allocated = 0, /* memory used by dbufs */ dbufs_used = 0, /* memory used by dbufs */ msg_allocated = 0, /* memory used by struct Msg */ msgbuf_allocated = 0, /* memory used by struct MsgBuf */ listenersm = 0, /* memory used by listetners */ rm = 0, /* res memory used */ totcl = 0, totch = 0, totww = 0, tot = 0; count_whowas_memory(&wwu, &wwm, &wwa, &wwam); wwm += sizeof(struct Whowas) * feature_uint(FEAT_NICKNAMEHISTORYLENGTH); wwm += sizeof(struct Whowas *) * WW_MAX; for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { c++; if (MyConnect(acptr)) { cn++; for (link = cli_confs(acptr); link; link = link->next) lcc++; } if (cli_user(acptr)) { for (inv = cli_user(acptr)->invited; inv; inv = inv->next_user) usi++; for (member = cli_user(acptr)->channel; member; member = member->next_channel) ++memberships; if (cli_user(acptr)->away) { aw++; awm += (strlen(cli_user(acptr)->away) + 1); } } #if defined(UNDERNET) if (IsAccount(acptr)) acc++; #endif } cm = c * sizeof(struct Client); cnm = cn * sizeof(struct Connection); user_count_memory(&us, &usm); for (chptr = GlobalChannelList; chptr; chptr = chptr->next) { ch++; chm += (strlen(chptr->chname) + sizeof(struct Channel)); for (ban = chptr->banlist; ban; ban = ban->next) { chb++; chbm += strlen(ban->who) + strlen(ban->banstr) + 2 + sizeof(*ban); } } for (aconf = GlobalConfList; aconf; aconf = aconf->next) { co++; com += aconf->host ? strlen(aconf->host) + 1 : 0; com += aconf->passwd ? strlen(aconf->passwd) + 1 : 0; com += aconf->name ? strlen(aconf->name) + 1 : 0; com += sizeof(struct ConfItem); } for (cltmp = get_class_list(); cltmp; cltmp = cltmp->next) cl++; #if defined(USE_SSL) send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Clients %d(%zu) Connections %d(%zu) SSL %d", c, cm, cn, cnm, ssl_count()); #else send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Clients %d(%zu) Connections %d(%zu)", c, cm, cn, cnm); #endif send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Users %zu(%zu) Accounts %d(%zu) Invites %d(%zu)", us, usm, acc, acc * (ACCOUNTLEN + 1), usi, usi * sizeof(struct Invite)); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":User channels %d(%zu) Aways %d(%zu)", memberships, memberships * sizeof(struct Membership), aw, awm); totcl = cm + cnm + us * sizeof(struct User) + memberships * sizeof(struct Membership) + awm; totcl += lcc * sizeof(struct SLink) + usi * sizeof(struct SLink); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Conflines %d(%zu) Attached %d(%zu) Classes %d(%zu)", co, com, lcc, lcc * sizeof(struct SLink), cl, cl * sizeof(struct ConnectionClass)); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Channels %d(%zu) Bans %d(%zu)", ch, chm, chb, chbm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Channel Members %d(%zu)", memberships, memberships * sizeof(struct Membership)); totch = chm + chbm; send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Whowas Users %d(%zu) Away %d(%zu) Array %u(%zu)", wwu, wwu * sizeof(struct User), wwa, wwam, feature_uint(FEAT_NICKNAMEHISTORYLENGTH), wwm); totww = wwu * sizeof(struct User) + wwam + wwm; watch_count_memory(&wt, &wtm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Watchs %d(%zu)", wt, wtm); motd_memory_count(cptr); gl = gline_memory_count(&glm); ju = jupe_memory_count(&jum); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Glines %d(%zu) Jupes %d(%zu)", gl, glm, ju, jum); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Hash: client %d(%zu), chan is the same", HASHSIZE, sizeof(void *) * HASHSIZE); count_listener_memory(&listeners, &listenersm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Listeners allocated %d(%zu)", listeners, listenersm); #if defined(DDB) ddb_count_memory(&dbs, &dbm); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":DDB keys allocated %d(%zu)", dbs, dbm); #endif /* * NOTE: this count will be accurate only for the exact instant that this * message is being sent, so the count is affected by the dbufs that * are being used to send this message out. If this is not desired, move * the dbuf_count_memory call to a place before we start sending messages * and cache DBufAllocCount and DBufUsedCount in variables until they * are sent. */ dbuf_count_memory(&dbufs_allocated, &dbufs_used); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":DBufs allocated %u(%zu) used %u(%zu)", DBufAllocCount, dbufs_allocated, DBufUsedCount, dbufs_used); /* The DBuf caveats now count for this, but this routine now sends * replies all on its own. */ msgq_count_memory(cptr, &msg_allocated, &msgbuf_allocated); rm = cres_mem(cptr); tot = totww + totch + totcl + com + cl * sizeof(struct ConnectionClass) + dbufs_allocated + msg_allocated + msgbuf_allocated + rm; tot += sizeof(void *) * HASHSIZE * 3; #if defined(MDEBUG) send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Allocations: %zu(%zu)", fda_get_block_count(), fda_get_byte_count()); #endif send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Total: ww %zu ch %zu cl %zu co %zu db %zu ms %zu mb %zu", totww, totch, totcl, com, dbufs_allocated, msg_allocated, msgbuf_allocated); }