siebeck@infoac.rmi.de (Wolfgang Siebeck ) (03/02/91)
Submitted-by: root@atreju.rmi.de (Wolfgang Siebeck) Archive-name: Mailutil/part01 Since I received several answers to my offer of these tools within two hours, I decided to post the stuff. Sorry about the documentation, this was designed for local use, so the comments are mainly German, but I tried to translate the important parts. There is a README file included, where I explain the idea behind the package. During the tests, I ran the package against the syslog of a SUN 3/50 and a Intel machine running SYS V. Both gave the desired results :-). Enjoy it and - well at least it should point to a way to interpret the syslog. Requests were coming from Dan Ehrlich, Kerry G. Forschler, David Asher, Matt Goldman, and darrah. Thank You for encouraging me to try my first posting! ---- Cut Here and feed the following to sh ---- #!/bin/sh # This is Mailutil, a shell archive (produced by shar 3.49) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # made 03/01/1991 16:08 UTC by root@atreju.rmi.de (Wolfgang Siebeck) # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 946 -rwxr-xr-x Mailutil/MailRech # 338 -rw-r--r-- Mailutil/Makefile # 6283 -rw-r--r-- Mailutil/README # 4565 -rw-r--r-- Mailutil/auswlog.awk # 13260 -rw------- Mailutil/auswlog.c # 4176 -rw-r--r-- Mailutil/justlog.c # 1359 -rw-r--r-- Mailutil/mailstat1.awk # 1769 -rw-r--r-- Mailutil/mailstat2.awk # 110 -rw-r--r-- Mailutil/mailutil.h # # ============= Mailutil/MailRech ============== if test ! -d 'Mailutil'; then echo 'x - creating directory Mailutil' mkdir 'Mailutil' fi if test -f 'Mailutil/MailRech' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/MailRech (File already exists)' else echo 'x - extracting Mailutil/MailRech (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/MailRech' && X : # # usage: # # MailRech syslogname [hostname] # # Generate invoices from syslog # X HOST=`hostname` if [ $# -gt 1 ] then X HOST=$2 fi export $HOST X grep sendmail $1 | justlog | sort > Sendmail.srt X # X auswlog Sendmail.srt $HOST rm -f Sendmail.srt if [ -f $HOST.out ] then X rm -f $HOST.out fi mv Sendmail.out $HOST.out echo "Input file for various statistics generated ($HOST.out)" X # # Ok, go for the money: # X # cat $HOST.out | nawk -f auswlog.awk host=$HOST > $HOST.bill X # # Since the script 'auswlog.awk' makes use of functions, only nawk # will work here. Anyway, it is a sample. You may try # the other samples like # # cat $HOST.out | awk -f mailstat1.awk # # for a summary with fancy graphics :-) # # or # # cat $HOST.out | awk -f mailstat2.awk # # for a sample with calculated costs. If you have nawk, You # might use the function round() to get real rounding instead # of the floating point format in printf. # X # X echo done. Xexit 0 SHAR_EOF chmod 0755 Mailutil/MailRech || echo 'restore of Mailutil/MailRech failed' Wc_c="`wc -c < 'Mailutil/MailRech'`" test 946 -eq "$Wc_c" || echo 'Mailutil/MailRech: original size 946, current size' "$Wc_c" fi # ============= Mailutil/Makefile ============== if test -f 'Mailutil/Makefile' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/Makefile (File already exists)' else echo 'x - extracting Mailutil/Makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/Makefile' && # # Makefile for the accounting utilities for # sendmails log. # X CC=gcc X CFLAGS=-O X LIBS= X .c.o: X $(CC) $(CFLAGS) -c $< X all: auswlog justlog X auswlog: auswlog.o X $(CC) $(CFLAGS) auswlog.o -o auswlog X justlog: justlog.o X $(CC) $(CFLAGS) justlog.o -o justlog X clean: X rm -f justlog.o auswlog.o core X clobber: clean X rm -f justlog auswlog SHAR_EOF chmod 0644 Mailutil/Makefile || echo 'restore of Mailutil/Makefile failed' Wc_c="`wc -c < 'Mailutil/Makefile'`" test 338 -eq "$Wc_c" || echo 'Mailutil/Makefile: original size 338, current size' "$Wc_c" fi # ============= Mailutil/README ============== if test -f 'Mailutil/README' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/README (File already exists)' else echo 'x - extracting Mailutil/README (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/README' && README for the syslog accounting package. X (Excuse my lousy english! I hope, You get the idea, anyway...) X First let me express my thanks for Your interest. I am glad to give some bytes in return to the information and help I got from the usenet community. X ----------------- X These utilities may be used to extract charging information from the file /usr/spool/mqueue/syslog. We are using this package in a commercial mailbox service. X Oh, I forgot: There are ANSI headers for gcc, the programs pass X gcc without warnings. X The provided shell script 'MailRech' is a kind of front end to the stuff. X This package is not a masterpiece in software engineering, but it serves us well. The base is: Never invent the wheel again. Use the Unix programs whereever they will do the job. So the way from the syslog to the accounting information is: X Step 1. use grep to extract the lines dealing with sendmail's output. Step 2. Reformat the lines, so they can be sorted on the queue-id. Step 3. Perform the sort. X What we have now, is a new logfile, in which only the relevant parts are contained. By sorting, multiple to= lines are grouped together and are easy to handle. X Step 4. Generate one log entry for each transmission. If one message is X sent to multiple receivers, generate one line each. X Step 5. Now comes the best: use awk to generate whatever you want. Awk X is not the best performer, but I am a real *fan* of it. Make bar X charts, write bills, summarize, ... or throw it away. X X Now we get the data: X Step 1. ------- X The input looks as follows (after grep sendmail ... ). The file is generated on a SUN 3/60. X ------------- X Jan 23 05:48:10 infohh: 6890 sendmail: AA06890: message-id=<9101230448.AA06890@infohh.rmi.de> Jan 23 05:48:10 infohh: 6890 sendmail: AA06890: from=percomp, size=1034, class=0 Jan 23 05:48:18 infohh: 6893 sendmail: AA06890: to=frisk@rhi.hi.is, delay=00:00:10, stat=Sent Jan 23 08:18:39 infohh: 7150 sendmail: AA07150: message-id=<9101230718.AA07150@infohh.rmi.de> Jan 23 08:18:39 infohh: 7150 sendmail: AA07150: from=news, size=107, class=0 Jan 23 08:18:41 infohh: 7153 sendmail: AA07150: to=root, delay=00:00:04, stat=Sent Jan 23 08:33:17 infohh: 7188 sendmail: AA07188: message-id=<9101230733.AA07188@infohh.rmi.de> Jan 23 08:33:17 infohh: 7188 sendmail: AA07188: from=natec, size=514, class=0 Jan 23 08:33:53 infohh: 7199 sendmail: AA07199: message-id=<9101230733.AA07199@infohh.rmi.de> Jan 23 08:33:53 infohh: 7199 sendmail: AA07199: from=natec, size=506, class=0 Jan 23 08:33:24 infohh: 7191 sendmail: AA07188: to=postmaster@sh.cs.net, delay=00:00:09, stat=Sent Jan 23 08:33:56 infohh: 7201 sendmail: AA07199: to=service@infohh.rmi.de, delay=00:00:04, stat=Sent X ------------- X Step 2. ------- X We feed this through justlog. All irrelevant information is stripped. (Notice the AA07188 and AA07199!) X -------------- AA06890:01:23:05:48:10:from=percomp, size=1034, class=0 AA06890:01:23:05:48:18:to=frisk@rhi.hi.is, delay=00:00:10, stat=Sent AA07150:01:23:08:18:39:from=news, size=107, class=0 AA07150:01:23:08:18:41:to=root, delay=00:00:04, stat=Sent AA07188:01:23:08:33:17:from=natec, size=514, class=0 AA07199:01:23:08:33:53:from=natec, size=506, class=0 AA07188:01:23:08:33:24:to=postmaster@sh.cs.net, delay=00:00:09, stat=Sent AA07199:01:23:08:33:56:to=service@infohh.rmi.de, delay=00:00:04, stat=Sent -------------- X Step 3. ------- X Sort the stuff. X -------------- AA06890:01:23:05:48:10:from=percomp, size=1034, class=0 AA06890:01:23:05:48:18:to=frisk@rhi.hi.is, delay=00:00:10, stat=Sent AA07150:01:23:08:18:39:from=news, size=107, class=0 AA07150:01:23:08:18:41:to=root, delay=00:00:04, stat=Sent AA07188:01:23:08:33:17:from=natec, size=514, class=0 AA07188:01:23:08:33:24:to=postmaster@sh.cs.net, delay=00:00:09, stat=Sent AA07199:01:23:08:33:53:from=natec, size=506, class=0 AA07199:01:23:08:33:56:to=service@infohh.rmi.de, delay=00:00:04, stat=Sent -------------- X Step 4. ------- X Reformat the file. Generate one line per mail. X -------------- percomp~01~23~05~48~1034~percomp~frisk@rhi~infohh~rhi news~01~23~08~18~107~news~root~infohh~infohh natec~01~23~08~33~514~natec~postmaster@sh~infohh~sh natec~01~23~08~33~506~natec~service@infohh~infohh~infohh -------------- X What we have now, may be the general purpose input. The fields are: X X 1. The user to be charged. (More on this later...) X 2. Month X 3. Day X 4. Hour X 5. Minute X 6. Bytes X 7. Sender X 8. Receiver X 9. Sending host 10. Receiving host X X X The charging algorithm: X We run a server for fax, telex, teletex, videotex and uucp message handling in Germany. Our own "network" are three Unix-based mailbox systems using this service as well as some MESS-DOS based systems. Fax, telex, teletex and videotex mail is charged seperately, so these messages and the confirmations are filtered. Furthermore, if a user sends a telex message, a copy of the message is archived in his box. Naturally, this is not charged. X Generally speaking, the sender has to pay. There are two exceptions: X X a) The message is from a remote host via uucp/smtp. We have to pay for X incoming mail, so the receiver on the local host is charged. X X b) A non-user may log into the system as user 'zczc' and write a X message to an user. So the receiver is charged for the online time X of Mr. ZCZC. X X If you don't need this information, just ignore the first field in the output and You are done. X X As a sample of analysis, some awk scripts for simple statistics are included in the package. X ------------- X Please redirect flames on the stuff to /dev/null, comments, critics or enhancements to X siebeck@infoac.rmi.de or postmaster@atreju.rmi.de X ====================================================================== X And here comes the standard disclaimer: X I do in no way guarantee, that these programs serve any purpose. I shall not be made responsible for loss of money, sanity, free disk space or whatever caused by these programs. All I can say is, I use them, and they work for me. X If you want to distribute the stuff, feel free to do so, but keep the stuff together. I don't claim special Copyrights or licensing fees on it, but please don't sell it, give it away for free. X ====================================================================== X SHAR_EOF chmod 0644 Mailutil/README || echo 'restore of Mailutil/README failed' Wc_c="`wc -c < 'Mailutil/README'`" test 6283 -eq "$Wc_c" || echo 'Mailutil/README: original size 6283, current size' "$Wc_c" fi # ============= Mailutil/auswlog.awk ============== if test -f 'Mailutil/auswlog.awk' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/auswlog.awk (File already exists)' else echo 'x - extracting Mailutil/auswlog.awk (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/auswlog.awk' && ######################################################################### # # Rechnungserstellung fuer Electronic Mail # # Version 1.0 # # (c) 1990 by RMI Nachrichtentechnik GmbH # ######################################################################### X function round(i) { X i = int((i*100) + 0.5)/100; X return i; } X ######################################################################### X function rechnung() { X if (rechnr > 0) X print("\n\n--------------------------------------\n"); X rechnr++; X ggeb = round(localmails[uname] * lmail); X hostlmail += ggeb; X X k = round(localbytes[uname] / 1024); X if ((k == 0) && (localmails[uname] > 0)) X k = 1; X mgeb = k * lkbyte; X hostlkbyte += mgeb; X X msteuer = 0.0; X X print ("Rechnung fuer Electronic Mail"); X printf("Username %s@%s\n\n\n", uname, host); X print (" Grund- Mengen-"); X print (" Mails Bytes gebuehr gebuehr"); X print ("-------------------------------------------"); X printf("local %5d %8d %8.2f %8.2f\n", \ X localmails[uname], \ X localbytes[uname], \ X ggeb, \ X mgeb); X k = round(remotebytes[uname] / 1024); X if ((k == 0) && (remotemails[uname] > 0)) X k = 1; X printf("rem. %5d %8d %8.2f %8.2f\n", \ X remotemails[uname], \ X remotebytes[uname], \ X round(remotemails[uname] * rmail), \ X round(remotebytes[uname] / 1024 * rkbyte)); X ggeb += round(remotemails[uname] * rmail); X hostrmail += round(remotemails[uname] * rmail); X X mgeb += round(k * rkbyte); X hostrkbyte += round(k * rkbyte); X X print ("-------------------------------------------"); X printf("total %5d %8d %8.2f %8.2f = %8.2f\n", \ X mails[uname], \ X bytes[uname], \ X ggeb, \ X mgeb, X ggeb + mgeb); X msteuer = round((ggeb + mgeb) * mwst); X printf(" + %2d%% MwSt = %8.2f\n", \ X mwst * 100, msteuer); X print (" --------"); X printf(" %8.2f\n", \ X ggeb + mgeb + msteuer); X print (" ========"); X hostmwst += msteuer; X # print ("Berechnungsgrundlage:"); # print ("---------------------"); # printf("Lokale Mail: %8.2f DM pro Mail\n", lmail); # printf(" %8.2f DM pro kByte\n", lkbyte); # printf("Remote Mail: %8.2f DM pro Mail\n", rmail); # printf(" %8.2f DM pro kByte\n", rkbyte); } X ######################################################################### X BEGIN { X FS="~"; X if (host == "") X host = "infohh"; X lmail = 0.0; X lkbyte = 0.0; X rmail = 0.05; X rkbyte = 0.20; X mwst = 0.14; X rechnr = 0; X hostlmail = 0.0; X hostlkbyte = 0.0; X hostrmail = 0.0; X hostrkbyte = 0.0; X hostmwst = 0.0; } X ######################################################################### X # # ## # # # ## ## # # # ## # # ## # # # # # # # # # ###### # # # # # # # # # # ## # # # # # # # X ######################################################################### /MAILER-DAEMON/ { X next } /\/dev\/null/ { X next } /news/ { X next } X { X if (substr($0,1,1) == "~") X next; X X mails[$1]++; X if ($9 == $10) { X localmails[$1]++; X localbytes[$1] += $6; X } X else { X remotemails[$1]++; X remotebytes[$1]+= $6; X } X bytes[$1] += $6; } X ######################################################################### X END { # i = 99; X for (usr in mails) { X uname = usr; X rechnung(); # if (i > 20) { # print("Username Mails Bytes"); # print("------------------------------------"); # i = 0; # } # printf("%-20s %6d %8d\n", usr, mails[usr], bytes[usr]); # i++; X } # Jetzt noch die Summen fuer den Host ausgeben: X print ("\n\n\n\n================================================\n"); X printf("Gesamtbetrag fuer den Host %s\n", host); X print (" Grund- Mengen-"); X print (" gebuehr gebuehr"); X print ("---------------------------"); X printf("local %8.2f %8.2f\n", hostlmail, hostlkbyte); X printf("remote %8.2f %8.2f\n", hostrmail, hostrkbyte); X print ("---------------------------"); X printf("Summe %8.2f %8.2f = %8.2f\n", \ X hostlmail+hostrmail, \ X hostlkbyte+hostrkbyte, \ X hostlmail+hostrmail+hostlkbyte+hostrkbyte); X msteuer = round((hostlmail+hostrmail+hostlkbyte+hostrkbyte) * mwst); X printf(" + %2d%% MwSt = %8.2f\n", \ X mwst * 100, msteuer); X print (" --------"); X printf(" %8.2f\n", \ X hostlmail+hostrmail+hostlkbyte+hostrkbyte+msteuer); X print (" ========"); X } SHAR_EOF chmod 0644 Mailutil/auswlog.awk || echo 'restore of Mailutil/auswlog.awk failed' Wc_c="`wc -c < 'Mailutil/auswlog.awk'`" test 4565 -eq "$Wc_c" || echo 'Mailutil/auswlog.awk: original size 4565, current size' "$Wc_c" fi # ============= Mailutil/auswlog.c ============== if test -f 'Mailutil/auswlog.c' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/auswlog.c (File already exists)' else echo 'x - extracting Mailutil/auswlog.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/auswlog.c' && /* ** Auswertung eines Log's, das bereits durch justlog ** und sort gegangen ist. ** ** WARNING: This program is a quick hack. It's designed for ** real addresses only. By saying real addresses, I ** talk about domain addressing, like user@host.domain. ** I did not try to decipher BANG's. ** ** The input must be sorted, so all AA.. items are grouped together. ** sort is also helpful, since the from= line will always be in front ** of the to= line. Care is taken for multiple to= lines per message ** and multiple receivers per to= line, as well. ** ** In the end, there will be one or more lines per message. If user ** joe_doe sends one message to five users, there will be five lines. ** ** The algorithm: ** ** Read input file, prepared by justlog and sorted. ** Make a clean line from input. ** No LF's or CR's, please. ** Initialize date and user information. ** Check, if new AA.. job. ** If yes, write new log file and initialize new line. ** Determine type of line (from= vs. to=) ** If from=, extract sender and msg-size. ** If to=, extract receiver(s). delay= and stat= are of ** no interest. (see justlog.c) ** Cleanup addresses (try to do so) ** Fill new log line according to information. ** until eof(). ** If there is still a new line to write, do so. ** ** The writing of the new log line might be a overkill for Your ** purpose. The underlying situation is described in the documentation. ** ** ** Updates: ** ** Version 1.1 added support for bang addressing. There is still ** a problem with addresses like blah%fasel!host.domain. ** ** Version 1.2 Since I had some problems, when compiling on a Sun ** I changed the declaration of LZ (Logzeile, ** means: log line) and NLZ (new log line). I had ** forgotten about the word alignment in nested ** structures. Now it really works. */ X #include <stdio.h> #include <string.h> #include <memory.h> X #include "mailutil.h" X /* length of various strings */ #define FNAMELEN 80 #define MAXLINELEN 256 #define MAXFROMLEN 160 #define MAXTOLEN 160 #define MAXNAMELEN 20 #define HOSTNAMELEN 10 X /* Types of syslog-lines we recognize */ #define UNKNOWN 0 #define FROM 1 #define TO 2 X /* What happens to the lines ? */ #define DROPIT 1 #define KEEPIT 0 X #ifndef DEBUG #define DEBUG 0 #endif X #define VERSION "1.2" X FILE *ifile, X *ofile; X #define KENNZSIZE 23 X /* Definition for read the old lines */ typedef union { X struct { X char kennz[8]; /* AA... von sendmail */ X char mon[3]; /* Monat u.s.w. */ X char day[3]; X char hour[3]; X char minute[3]; X char second[3]; X char rest[MAXLINELEN - 22]; /* from= und to= Zeile */ X } inh; X char alles[MAXLINELEN + 1]; } LZ, *LZPTR; X /* Definition for writing the new lines */ typedef struct { X char kennz[8]; /* AA... von sendmail */ X char mon[3]; /* Monat u.s.w. */ X char day[3]; X char hour[3]; X char minute[3]; X char second[3]; X char zahler[MAXNAMELEN + 1]; /* Zahlungspflichtiger */ X char from[MAXFROMLEN + 1]; /* Absender der Nachricht */ X char to[MAXTOLEN + 1]; /* Empfaenger der Nachricht */ X long len; /* Laenge der Nachricht */ } NLZ, *NLZPTR; X char defaulthost[HOSTNAMELEN + 1]; X /* ** Aufraeumen in der Eingabezeile */ X void clearline(LZPTR lz) { X char *s; X X if (s = strrchr(lz->alles, '\r')) /* You never know, but this */ X *s = '\0'; /* might run under MESS-DOS */ X if (s = strrchr(lz->alles, '\n')) X *s = '\0'; X lz->inh.kennz[7] = X lz->inh.mon[2] = X lz->inh.day[2] = X lz->inh.hour[2] = X lz->inh.minute[2] = X lz->inh.second[2] = '\0'; } X /* ** Determine type of log line */ X int gettype(LZPTR lz) { X #if DEBUG X printf("Rest:%10.10s\n", lz->inh.rest); #endif X X if (!strncmp(lz->inh.rest, "to=", 3)) X return TO; X if (!strncmp(lz->inh.rest, "from=", 5)) X return FROM; X return UNKNOWN; } X /* ** Determine, if a log line is to be dropped */ X int dropthis(NLZPTR nlz, char *empfhost) { X /* Es fallen keine Gebuehren bei: */ X /* 1. Qsl's, Confirmations */ X if (!strncmp(nlz->to, "qsl@", 4)) X return DROPIT; X X /* 2. Eingehende Telexe, incoming telexes */ X if (!strncmp(nlz->to, "ttxin@", 6)) X return DROPIT; X X /* 3. Abgehende Faxe, outgoing faxes */ X if (!strcmp(empfhost, "fax")) X return DROPIT; X X /* 4. "Ge-pipe-te" Sachen, stuff piped into programs */ X if (!strncmp(nlz->to, "\"|", 2)) X return DROPIT; X X /* 5. Abgehende Telexe, outgoing telexes */ X if (!strncmp(nlz->to, "ttx@rmi", 7)) X return DROPIT; X X /* 6. Mail an sich selbst, z.B. archivierte Telexe, archived telexes */ X if (!strcmp(nlz->from, nlz->to)) X return DROPIT; X X return KEEPIT; } X /* ** Wegschreiben einer Zeile in das neue Log. ** ** Es gibt pro Nachricht eine oder mehrere Zeilen, je nach ** dem, wieviele Empfaenger existieren. ** ** Vor dem Wegschreiben wird noch ueberprueft, ob der Absender ** oder der Empfaenger die Gebuehren tragen soll. Als Erkennung gilt ** dabei: ** ** Feststellen des Absender-Hosts und des Empfaenger-Hosts. ** Ist kein Host zu finden, so wird der default eingesetzt. ** ------------------------- ** Write one line to the new log file. For each message, there may ** be one or more lines according to the number of receivers. ** ** Check for sending and receiving hosts. If no host is found, ** insert name of default host (local mail). */ X void writelog(NLZPTR nlz) { X char abshost[HOSTNAMELEN + 1], X empfhost[HOSTNAMELEN + 1], X zahler[MAXLINELEN + 1]; X char *hname, X *punkt; X X hname = strchr(nlz->from, '@'); /* user@host.domain */ X if (hname) { X hname++; X if ((punkt = strchr(hname, '.')) != NULL) X *punkt = '\0'; X strncpy(abshost, hname, HOSTNAMELEN); X abshost[HOSTNAMELEN] = '\0'; X } else X strcpy(abshost, defaulthost); X X hname = strchr(nlz->to, '@'); X if (hname) { X hname++; X if ((punkt = strchr(hname, '.')) != NULL) X *punkt = '\0'; X strncpy(empfhost, hname, HOSTNAMELEN); X empfhost[HOSTNAMELEN] = '\0'; X } else X strcpy(empfhost, defaulthost); X /* ** Now: If neither the receiving nor the sending host ** is the defaulthost, the mail was just routed and the ** accounting will be done on another host. */ X X if (strcmp(empfhost, defaulthost) && strcmp(abshost, defaulthost)) X return; X X if (dropthis(nlz, empfhost) == DROPIT) X return; X X /* Who should pay? By default, it is the sender! */ X strcpy(zahler, nlz->from); X X /* Except in case the receiver is on the default host ... */ X if (!strcmp(empfhost, defaulthost)) { X X /* but only, if not local! */ X if (strcmp(abshost, empfhost)) X strcpy(zahler, nlz->to); X } X if ((punkt = strchr(zahler, '@')) != NULL) X *punkt = '\0'; X /* ** Und wieder ein Sonderfall: Falls der Absender ** zczc ist, muss auch der Empfaenger zahlen! (Mail from Mr. ZCZC) */ X if (!strcmp(nlz->from, "zczc")) { X strcpy(zahler, nlz->to); X } X strncpy(nlz->zahler, zahler, MAXNAMELEN); X nlz->zahler[MAXNAMELEN] = '\0'; X #if DEBUG X printf("%s~%s~%s~%s~%s~%ld~%s~%s\n", X nlz->zahler, X nlz->mon, X nlz->day, X nlz->hour, X nlz->minute, X nlz->len, X nlz->from, X nlz->to); #endif X X fprintf(ofile, "%s~%s~%s~%s~%s~%ld~%s~%s~%s~%s\n", X nlz->zahler, X nlz->mon, X nlz->day, X nlz->hour, X nlz->minute, X nlz->len, X nlz->from, X nlz->to, X abshost, X empfhost); } X /* ** Try to clean up an address. ** ** This doesn't look to good, but bang address are bad manners anyway. */ X void clean_address(char *addr) { X char wbuff[MAXTOLEN + 1], X *s1, X *s2, X host[HOSTNAMELEN + 1], X user[MAXNAMELEN + 1]; X X strcpy(wbuff, addr); /* Work on temporary buffer */ X if ((s1 = strchr(wbuff, '@')) != NULL) X return; /* assume standard format */ X X if ((s1 = strrchr(wbuff, '!')) != NULL) { X strncpy(user, &s1[1], MAXNAMELEN); X user[MAXNAMELEN] = '\0'; X if ((s2 = strchr(wbuff, '!')) != NULL) { X if (s1 == s2) { /* We have host!user */ X *s2 = '\0'; X strncpy(host, wbuff, HOSTNAMELEN); X host[HOSTNAMELEN] = '\0'; X } X /* Here we have a blah!fasel!host!user */ X s2 = s1; /* ^ */ X for (*s2 = '\0'; (s2 >= wbuff) && (*s2 != '!'); s2--); X if (*s2 == '!') { X strncpy(host, &s2[1], HOSTNAMELEN); X host[HOSTNAMELEN] = '\0'; X if ((s1 = strchr(host, '.')) != NULL) X /* blah!fasel!host.domain!user */ X /* ^ */ X *s1 = '\0'; X } X sprintf(addr, "%s@%s", user, host); X return; X } X } } X /* ** Zerlegen der from= Zeile aus dem Log. ** ** Gefuellt wird das Absender/Laengenfeld ** ** Interpret the from= line. ** We extract the sending user and the file size */ X void parsefrom(LZPTR lz, NLZPTR nlz) { X char delim = ','; /* Delimiter fuer den Namen */ X int anfang = 5; X char *src, X *dest; X X memcpy(nlz, lz, KENNZSIZE); /* copy the ID-part */ X if (lz->inh.rest[anfang] == '<') { X delim = '>'; X anfang++; X } X src = &(lz->inh.rest[anfang]); X dest = &(nlz->from[0]); X while (*src != delim) { /* copy senders name */ X *dest = *src; X dest++; X src++; X } X *dest = '\0'; X clean_address(&(nlz->from[0])); /* ** Jetzt noch die Groesse der Nachricht setzen ** src zeigt mindestens auf das Ende des Absenders ** Get size of message */ X while (*src) { X if (!strncmp(src, "size=", 5)) { X /* Groesse gefunden! */ X src += 5; X nlz->len = atol(src); X return; X } X src++; X } X } X /* ** Zerlegen einer to= Zeile. ** ** Es interessiert hiervon nur der echte to= Eintrag, ** delay= und stat= werden vernachlaessigt. ** ** Es koennen zu einer Nachricht mehrere to= Zeilen auftreten, ** eine to= Zeile kann aber auch mehrere Empfaenger haben. ** Sollte der to-Eintrag in nlz bereits belegt sein, so ** muss dieser erst weggeschrieben werden, bevor der neue ** Eintrag eingesetzt wird. ** ** Interpret the to= line. ** ** All we need is the to=receiver part. */ X void parseto(LZPTR lz, NLZPTR nlz) { X char delim = ','; /* Delimiter fuer den Namen */ X int anfang = 3; X char *src, X *dest; X X if (lz->inh.rest[anfang] == '<') { X delim = '>'; X anfang++; X } X src = &(lz->inh.rest[anfang]); X dest = &(nlz->to[0]); X if (*dest != '\0') { /* There is one already */ X writelog(nlz); /* so save that line */ X memset(nlz->to, 0, MAXTOLEN + 1); X } X for (; *src;) { X while (*src != delim) { /* Copy receiver */ X *dest = *src; X dest++; X src++; X } X *dest = '\0'; X clean_address(&(nlz->to[0])); X #if DEBUG X printf("Empfaenger:%s<\n", nlz->to); #endif X X /* Ok, but there might be another receiver */ X while (*src) { X if (*src == ',' || *src == ' ' || *src == delim) { X src++; X continue; X } X #if DEBUG X printf("src:%s<\n", src); X getchar(); #endif X X if (!strncmp(src, "delay=", 6)) /* We're done */ X return; X writelog(nlz); /* This line to log */ X memset(nlz->to, 0, MAXTOLEN + 1); X dest = &(nlz->to[0]); X if (*src == '<') { X delim = '>'; X src++; X } else X delim = ','; X break; X } X } X } X /* ** Parsen einer Logzeile ** ** Jede Zeile aus dem Log geht hier hinein. Sie wird dann ** aufgrund des Kennzeichens zugeordnet. Pro versandte Nachricht ** entsteht so wieder eine Logzeile, die einen definierten Aufbau ** hat. ** Falls eine Nachricht an mehrere Empfaenger geht, entstehen auch ** mehrere Zeilen im neuen Log. Um die Zuordnung zum Zahler zu ** ermoeglichen, wird aus dem to= oder from= der Zahlungspflichtige ** in den Eintrag "zahler" eingesetzt. ** ----------------------- ** See 'algorithm' at BOF. */ X void parseline(LZPTR lz, NLZPTR nlz) { X static char okz[16]; X char nkz[16]; X int nlztype; X X memcpy(nkz, lz, 15); X nkz[15] = '\0'; X if (strcmp(nkz, okz)) { /* New message-id or date */ X if (okz[0] != '\0') { /* but not the first one! */ X writelog(nlz); X memset(nlz, 0, sizeof (NLZ)); X } X memcpy(okz, nkz, sizeof(okz)); X #if DEBUG X printf("%s\n", nkz); #endif X } X nlztype = gettype(lz); X #if DEBUG X printf("Type: %d\n", nlztype); #endif X X switch (nlztype) { X case FROM: /* Next sender */ X parsefrom(lz, nlz); X break; X case TO: X parseto(lz, nlz); X break; X } } X void main(int argc, char *argv[]) { X LZ ibuf; X NLZ nlz; X char *s, X oname[FNAMELEN]; X X if (argc != 3) { X fprintf(stderr, "%s %s\n", argv[0], VERSION); X fprintf(stderr, "usage: %s infile hostname\n", argv[0]); X fprintf(stderr, "convert infile to usable input for any\n"); X fprintf(stderr, "accounting purpose.\n\n"); X fprintf(stderr, "infile must be prepared by 'justlog'.\n"); X exit(1); X } X if ((ifile = fopen(argv[1], "r")) == NULL) { X printf("\"%s\" not found.\n", argv[1]); X exit(1); X } X strcpy(oname, argv[1]); X s = strrchr(oname, '.'); X if (s) X *s = '\0'; X strcat(oname, ".out"); X X if ((ofile = fopen(oname, "w")) == NULL) { X printf("Can't open output \"%s\".\n", oname); X fclose(ifile); X exit(1); X } X strcpy(defaulthost, argv[2]); X X while (fgets(ibuf.alles, MAXLINELEN, ifile)) { X clearline(&ibuf); X parseline(&ibuf, &nlz); X } X if (nlz.to[0] != '\0') /* The last one is to be written */ X writelog(&nlz); X fclose(ifile); X fclose(ofile); } SHAR_EOF chmod 0600 Mailutil/auswlog.c || echo 'restore of Mailutil/auswlog.c failed' Wc_c="`wc -c < 'Mailutil/auswlog.c'`" test 13260 -eq "$Wc_c" || echo 'Mailutil/auswlog.c: original size 13260, current size' "$Wc_c" fi # ============= Mailutil/justlog.c ============== if test -f 'Mailutil/justlog.c' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/justlog.c (File already exists)' else echo 'x - extracting Mailutil/justlog.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/justlog.c' && /* ** Filter zur Umformatierung von syslog's ** zur Auswertung und Berechnung der Mailkosten ** ** The algorithm: ** ** Get a line from syslog ** Convert the date from 'verbose' to numeric format. ** Drop stuff like 'new aliases ...' ** Get the AA.. part of the line. ** Get the useful information (from=, to=, size=, stat=) ** Drop lines, if the to= line does not contain stat=Sent. ** Drop lines with message-id= ** Move the AA.. part to the beginning of the line. ** (We will need this later for sorting the stuff!) ** Output the converted line. ** until eof(). */ X #include <stdio.h> #include <string.h> X #include "mailutil.h" X /* Max. Length exspected in the syslog */ #define LINELEN 256 X #define VERSION "1.0" X static char *monate[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", X NULL }; X /* ** Teil eins der Konvertierung. ** ** 1. Monat aus dem Klartext in lfd. Nummer uebersetzen ** 2. Zeilen wie newaliases ausfiltern ** 3. Die PID von sendmail ausfiltern ** 4. Im Datenteil (hinter AA...) alle Blanks loeschen ** 5. Blanks im Datum durch ':' ersetzen ** ** Uebergabeparameter: ** char *in Zeiger auf Eingabezeile ** char *out Zeiger auf Ausgabezeile ** ** Rueckgabe: ** 0 Konvertierung ok. ** 1 Zeile ist zu verwerfen. ** ** ------------------- ** Part one of conversion ** ** Translate verbose date to numbers ** filter lines like 'blah new aliases' etc. ** strip blanks from the relevant part after AA... ** replace all blanks in date by ':' */ X int convert_buff(char *in, char *out) { X int i; X char *s, X *so; X X for (i = 0; monate[i]; i++) { X if (!strncmp(in, monate[i], 3)) { X sprintf(out, "%02d", i + 1); X break; X } X } X if (monate[i] == NULL) /* should never happen */ X strcpy(out, "13"); X s = in + 3; /* start behind month */ X so = out + 2; X for (i = 0; i < 12; i++) { /* and copy date */ X *so = *s; X s++; X so++; X } X /* next thing to get is the AA... part */ X s = in + 15; X while (*s) { X if ((*s == ':') && (!strncmp(s, ": AA", 4))) X break; X s++; X } X if (!*s) /* No AA... found */ X return 1; X *so++ = ':'; X s += 2; X while (*s) { /* copy rest of line */ X *so = *s; X so++; X s++; X } X *so = '\0'; X *(out + 14) = ':'; /* insert ':' after time */ X if (*(out + 3) == ' ') /* make day numeric */ X *(out + 3) = '0'; X for (s = out, i = 0; *s && (i < 15); i++, s++) X if (*s == ' ') X *s = ':'; X return 0; } X /* ** "Verdrehen" einer Zeile wg, Sortierung ** ** 1. Die AA... - Angabe von sendmail wird nach vorne gerueckt. ** 2. Zeilen mit "message-id" oder "locked" werden verworfen. ** 3. to= Zeilen, in denen nicht stat=Sent steht, werden auch ** verworfen. ** ** Uebergabeparameter: ** char buf[] Adresse der Zeile ** ** Rueckgabe: ** Adresse des statischen Buffers mit gedrehter Zeile, ** im "verwerflichen" Fall NULL ** ------------------- ** Make lines sortable ** ** Move AA... as key to beginning of line ** drop lines containing 'message-id=' or 'locked' ** drop lines containing 'to=' AND 'stat!=Sent' ** i.e. Deferred, etc. */ X char * drehbuf(char buf[]) { X static char workbuf[LINELEN + 1], X *s; X X sprintf(workbuf, "%8.8s%15.15s%s", &buf[15], buf, &buf[24]); X s = &workbuf[23]; X if ((!strncmp(s, "locked", 6)) || (!strncmp(s, "message-id=", 11))) X return NULL; X if (!strncmp(s, "to=", 3)) { X /* This line should contain stat=Sent */ X while (*s) { X if (!strncmp("stat=", s, 5)) { X s += 5; X if (strncmp("Sent", s, 4)) X return NULL; X } X s++; X } X } X return workbuf; X } X void main(int argc, char *argv[]) { X char inbuf[LINELEN + 1], X outbuf[LINELEN + 1], X *s; X X if (isatty(fileno(stdin)) || isatty(fileno(stdout))) { X fprintf(stderr, "%s %s\n", argv[0], VERSION); X fprintf(stderr, "A filter for use on syslog for gathering\n"); X fprintf(stderr, "information about mail costs. Use as filter!\n"); X exit(1); X } X while (gets(inbuf)) { X if (strlen(inbuf) < 30) /* useless information */ X continue; X if (!convert_buff(inbuf, outbuf)) { X s = drehbuf(outbuf); X if (s) X puts(s); X } X } X } SHAR_EOF chmod 0644 Mailutil/justlog.c || echo 'restore of Mailutil/justlog.c failed' Wc_c="`wc -c < 'Mailutil/justlog.c'`" test 4176 -eq "$Wc_c" || echo 'Mailutil/justlog.c: original size 4176, current size' "$Wc_c" fi # ============= Mailutil/mailstat1.awk ============== if test -f 'Mailutil/mailstat1.awk' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/mailstat1.awk (File already exists)' else echo 'x - extracting Mailutil/mailstat1.awk (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/mailstat1.awk' && ######################################################################### # # Rechnungserstellung fuer Electronic Mail # # Version 1.0 # # (c) 1990 by RMI Nachrichtentechnik GmbH # ######################################################################### X X BEGIN { X FS="~"; } X ######################################################################### X /MAILER/ { X next } /\/dev\/null/ { X next } /news/ { X next } X { X if (substr($0,1,1) == "~") X next; X mails[$1]++; X if ($9 == $10) { X localmails[$1]++; X localbytes[$1] += $6; X } X else { X remotemails[$1]++; X remotebytes[$1]+= $6; X } X bytes[$1] += $6; } X ######################################################################### X END { X max = 0; X tmail = 0; X tbytes = 0; X for (usr in mails) { X tmail += mails[usr]; X tbytes += bytes[usr]; X if (bytes[usr] > max) X max = bytes[usr]; X } X X for (usr in mails) { X end = bytes[usr] / max * 60; X if (end > 2) { # don't graph the underdogs X printf("%-14s", usr); X for (i = 0; i < end; i++) X printf("#"); X print(""); X } X } X print("\n---\n"); X print("User Mails Bytes"); X print("------------------------------"); X for (usr in mails) { X printf("%-14s %6d %8d\n", usr, mails[usr], bytes[usr]); X } X print("------------------------------"); X printf("%-14s %6d %8d\n", "Total", tmail, tbytes); X print("=============================="); X } SHAR_EOF chmod 0644 Mailutil/mailstat1.awk || echo 'restore of Mailutil/mailstat1.awk failed' Wc_c="`wc -c < 'Mailutil/mailstat1.awk'`" test 1359 -eq "$Wc_c" || echo 'Mailutil/mailstat1.awk: original size 1359, current size' "$Wc_c" fi # ============= Mailutil/mailstat2.awk ============== if test -f 'Mailutil/mailstat2.awk' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/mailstat2.awk (File already exists)' else echo 'x - extracting Mailutil/mailstat2.awk (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/mailstat2.awk' && ######################################################################### # # Rechnungserstellung fuer Electronic Mail # # Version 1.0 # # (c) 1990 by RMI Nachrichtentechnik GmbH # ######################################################################### X #function round(i) { # i = int((i*100) + 0.5)/100; # return i; #} X ######################################################################### X BEGIN { X FS="~"; X if (host == "") X host = "infohh"; X lmail = 0.1; X lbyte = 0.00001; X rmail = 0.05; X rbyte = 0.0002; X mwst = 0.14; X rechnr = 0; X hostlmail = 0.0; X hostlkbyte = 0.0; X hostrmail = 0.0; X hostrkbyte = 0.0; X hostmwst = 0.0; } X ######################################################################### X /MAILER-DAEMON/ { X next } /\/dev\/null/ { X next } /news/ { X next } X { X if (substr($0,1,1) == "~") X next; X X mails[$1]++; X if ($9 == $10) { X localmails[$1]++; X localbytes[$1] += $6; X } X else { X remotemails[$1]++; X remotebytes[$1]+= $6; X } X bytes[$1] += $6; } X ######################################################################### X END { X i = 99; X for (usr in mails) { X if (i > 20) { X print(" l o c a l r e m o t e"); X print("Username Mails Bytes Mails Bytes local remote total"); X print("-------------------------------------------------------------------------------"); X i = 0; X } X lcost = localmails[usr] * lmail + localbytes[usr] * lbyte; X rcost = remotemails[usr] * rmail + remotebytes[usr] * rbyte; X printf("%-20s %6d %8d %6d %8d %8.2f %8.2f %8.2f\n", usr, \ X localmails[usr], localbytes[usr], \ X remotemails[usr], remotebytes[usr], \ X lcost, rcost, lcost + rcost); X i++; X } X print("-------------------------------------------------------------------------------"); X } SHAR_EOF chmod 0644 Mailutil/mailstat2.awk || echo 'restore of Mailutil/mailstat2.awk failed' Wc_c="`wc -c < 'Mailutil/mailstat2.awk'`" test 1769 -eq "$Wc_c" || echo 'Mailutil/mailstat2.awk: original size 1769, current size' "$Wc_c" fi # ============= Mailutil/mailutil.h ============== if test -f 'Mailutil/mailutil.h' -a X"$1" != X"-c"; then echo 'x - skipping Mailutil/mailutil.h (File already exists)' else echo 'x - extracting Mailutil/mailutil.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Mailutil/mailutil.h' && /* ** Prototypes for external functions */ X void exit(int result); long atol(char *src); int isatty(int fh); X SHAR_EOF chmod 0644 Mailutil/mailutil.h || echo 'restore of Mailutil/mailutil.h failed' Wc_c="`wc -c < 'Mailutil/mailutil.h'`" test 110 -eq "$Wc_c" || echo 'Mailutil/mailutil.h: original size 110, current size' "$Wc_c" fi exit 0 -- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | Auch: wsiebeck@rmi.de | Hier koennte | | siebeck@infoac.rmi.de | Ihre Anzeige | | root@atreju.rmi.de | stehen | =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=