/*
* wuzzah.c
*
* written and (c) 2001 by Sean Finney
*
* This program can be freely copied and redistributed under
* the terms of the GNU General Public License, v2. For details
* of said terms, please see the file COPYING
*
*/
// Includes
#include "includes.h"
#include "buddies.h"
#include "hashtable.h"
#include "common.h"
// Definitions
// Macros
// Function Prototypes
void usage(void);
void version(void);
void wuzzah_whoami(void);
void wuzzah_add_extrabuddies(const char *line, htable_t *ht);
// Global (config) Variables
// g_progname: the name of this program (basename(argv[0]))
// g_infile: file to be read in
// g_buddy_msg: message to be sent to buddy
// g_status_msg: message to be displayed on local tty
// g_write_users: whether or not to send users a message
// g_sleep_interval: how long to sleep between polling utmp
// g_whoami: local username
// g_no_newline: don't hard-handedly add a newline to the mesg
// g_eventcmd: a command to run when a buddy logs on/off
wuzzah_config_t g_config;
static const char *g_default_buddy_msg=
"(wuzzah) %u says: \"shoutout to my homie %b.\"";
static const char *g_default_status_msg=
"(%d) %b %o %l from %h";
static const char *g_default_infile=".wuzzah";
static const int g_default_sleep_interval=1;
struct option program_opts[] = {
{ "all-users",1,0,'a' },
{ "exec-command",1,0,'c' },
{ "help",0,0,'h' },
{ "buddyfile",1,0,'f' },
{ "no-buddyfile",0,0,'F'},
{ "interval",1,0,'i' },
{ "message",1,0,'m' },
{ "no-newline",0,0,'n' },
{ "process-once",0,0,'o' },
{ "process-current",0,0,'p' },
{ "silent",0,0,'q' },
{ "status-message",1,0,'s' },
{ "users",1,0,'u' },
{ "version",0,0,'v' },
{ "write-buddies",0,0,'w' },
{ NULL, 0, 0, 0}
};
extern char *optarg;
extern int optind;
// ---- MAIN ----
int main(int argc, char *argv[]){
htable_t *ht=NULL;
process_args(argc, argv, &g_config);
ht=bud_init_budtable();
wuzzah_whoami();
if(g_config.all_users){
bud_load_every_user(ht);
} else {
if(g_config.extrabuddies)
wuzzah_add_extrabuddies(g_config.extrabuddies, ht);
if(!g_config.noloadfile) bud_load(g_config.infile, ht);
}
if(ht->size==0){
fprintf(stderr,
"%s: no buddies specified\n", g_config.progname);
fprintf(stderr, "well, if you don't have any friends for\n");
fprintf(stderr, "me to watch, I'm going away!\n");
exit(1);
}
// let's do it once first, and not spam everyone when
// we start up (hence 0).
if(!g_config.process_current) bud_chk_utmpx(ht, -1);
else bud_chk_utmpx(ht, g_config.write_users);
// if we were only supposed to go once
if(g_config.run_once) return 0;
// otherwise...
while(!sleep(g_config.sleep_interval)){
bud_chk_utmpx(ht, g_config.write_users);
}
return 0;
}
// ---- Other Functions ----
void process_args(int ac, char *av[], wuzzah_config_t *conf){
int c, len;
char *homedir;
optind=0;
memset(conf, 0, sizeof(wuzzah_config_t));
conf->progname=av[0]=basename(av[0]);
while(1){
c=getopt_long(ac, av, "ac:f:Fhi:m:nopqs:u:vw",
program_opts, NULL);
switch(c){
case 'a': conf->all_users=1; break;
case 'c': conf->eventcmd=strdup(optarg); break;
case 'f': conf->infile=strdup(optarg); break;
case 'F': conf->noloadfile=1; break;
case 'h': version(); usage(); exit(0); break;
case 'i': conf->sleep_interval=atoi(optarg); break;
case 'm': conf->buddy_msg=strdup(optarg); break;
case 'n': conf->no_newline=1; break;
case 'o': conf->run_once=1;
case 'p': conf->process_current=1; break;
case 'q': conf->write_users=0; break;
case 's': conf->status_msg=strdup(optarg); break;
case 'u': conf->extrabuddies=strdup(optarg); break;
case 'v': version(); exit(0); break;
case 'w': conf->write_users=1; break;
}
if(c==-1)break;
}
if(!conf->buddy_msg)conf->buddy_msg=g_default_buddy_msg;
if(!conf->status_msg)conf->status_msg=g_default_status_msg;
// if the userfile hasn't been specified, use ${HOME}/.wuzzah
if(!conf->infile && !conf->noloadfile){
homedir=getenv("HOME");
len=strlen(homedir)+1+strlen(g_default_infile); // 1 for '/'
conf->infile=(char*)malloc(sizeof(char)*(len+1));
strncpy(conf->infile, homedir, strlen(homedir)+1);
strncat(conf->infile, "/", 1);
strncat(conf->infile, g_default_infile,
strlen(g_default_infile));
}
if(!conf->sleep_interval)
conf->sleep_interval=g_default_sleep_interval;
}
/*
* gaaaah, my eyes, this is so ugly. please make me prettier without
* losing any functionality...
*
*/
int string_to_argv(const char *str, char **av[]){
int c=0, i=0, j=0, num_args=1, len=strlen(str), procd_str_pipe[2];
int arg_len=0, arg_max_len=0;
short parsing_whitespace=1;
char **argv=NULL, *tmp=strdup(str);
FILE *p_write, *p_read;
if(pipe(procd_str_pipe)) perror("opening pipe");
p_read=fdopen(procd_str_pipe[0], "r");
if(!p_read) perror("opening read end of pipe");
p_write=fdopen(procd_str_pipe[1], "w");
if(!p_write) perror("opening write end of pipe");
for(i=0;i<len;i++){
switch(str[i]){
case '\\':
if(parsing_whitespace) {
num_args++;
parsing_whitespace=0;
}
if(i==len-1) {
fprintf(stderr,
"syntax err: unterminated \\\n");
return -1;
}
fputc(str[i++], p_write);
fputc(str[i], p_write);
arg_len+=2;
break;
case ' ': case '\n': case '\t':
if(!parsing_whitespace){
if(arg_len>arg_max_len)arg_max_len=arg_len;
arg_len=0;
fputc('\0', p_write);
parsing_whitespace=1;
}
break;
case '\'':
if(parsing_whitespace) {
num_args++;
parsing_whitespace=0;
}
while(str[i+1] != '\'' || str[i] == '\\'){
if(++i > len){
fprintf(stderr,
"error: unterminated '\n");
return -1;
}
if(str[i+1]=='\'' && str[i] == '\\') continue;
else {
arg_len++;
fputc(str[i], p_write);
}
}
i++;
break;
case '"':
if(parsing_whitespace) {
num_args++;
parsing_whitespace=0;
}
while(str[i+1] != '"' || str[i] == '\\'){
if(++i > len){
fprintf(stderr,
"error: unterminated \"\n");
return -1;
}
if(str[i+1]=='"' && str[i] == '\\') continue;
else {
arg_len++;
fputc(str[i], p_write);
}
}
i++;
break;
default:
if(parsing_whitespace) {
num_args++;
parsing_whitespace=0;
}
arg_len++;
fputc(str[i], p_write);
break;
}
}
fclose(p_write);
argv=(char **)malloc(sizeof(char*)*(num_args+1));
memset(argv, 0, sizeof(char)*(num_args+1));
argv[0]=strdup(g_config.progname);
for(i=1; i<num_args; i++){
argv[i]=(char*)malloc(sizeof(char)*(arg_max_len+1));
memset(argv[i], 0, sizeof(char)*(arg_max_len+1));
c=fgetc(p_read);
for(j=0; c!='\0' && !feof(p_read) && !ferror(p_read); j++){
argv[i][j] = c;
c=fgetc(p_read);
}
argv[i][j]='\0';
}
free(tmp);
argv[num_args]=NULL;
*(av)=argv;
return num_args;
}
void wuzzah_whoami(void){
struct passwd *pwd=getpwuid(getuid());
// get the user's name as gracefully as we can :)
if(!pwd){
fprintf(stderr,
"uh, your uid (%d) wasn't found..\n", (int)getuid());
fprintf(stderr, "so I'm setting your name to \"dumbass\".\n");
g_config.whoami=(char*)malloc(sizeof(char)*8);
strncpy(g_config.whoami, "dumbass", 8);
} else {
g_config.whoami=(char*)malloc(sizeof(char)*(strlen(pwd->pw_name)+1));
strncpy(g_config.whoami, pwd->pw_name, (strlen(pwd->pw_name)+1));
}
}
void wuzzah_add_extrabuddies(const char *line, htable_t *ht){
int i=0, len=strlen(line);
const char *head=line;
char *buf=NULL;
buf=(char*)malloc(sizeof(char)*(strlen(line)+1));
while((int)(head-line)<len){
i=strcspn(head, " \t\n,:");
if(i>0) {
strncpy(buf, head, i);
buf[i]='\0';
} else {
strncpy(buf, head, strlen(head));
buf[strlen(head)]='\0';
}
ht_insert(bud_create_buddy(buf), ht);
head=&head[i+1];
}
free(buf);
}
void version(void){
fprintf(stderr, "%s v%s\t-\twuuzzzaaah?\n", g_config.progname,
PACKAGE_VERSION);
fprintf(stderr, "copyright 2002 sean finney <seanius@seanius.net>\n");
}
void usage(void){
fprintf(stderr, "\nusage:\t");
fprintf(stderr, "%s [OPTIONS] ...\n", g_config.progname);
fprintf(stderr, ""
" -h, --help this informative usage summary :)\n"
" -v, --version the current version and copyright\n\n"
" -a, --all-users load and watch for all users\n"
" -c, --exec-cmd=CMD evaluate/execute CMD with every login\n"
" -f, --buddyfile=FILE use FILE as buddyfile, can be '-' for\n"
" signifying to use stdin.\n"
" -F, --no-buddyfile do not attempt to load the file which\n"
" contains the list of buddies to watch\n"
" -i, --interval=NUM sleep NUM seconds between each polling\n"
" -m, --message=STRING use STRING as a message template to greet\n"
" logged-in buddies. RTFM for more information\n"
" -n, --no-newline don't end the various messages in newlines\n"
" -o, --process-once exit after having scanned once through the\n"
" records.\n"
" -p, --process-current override wuzzah's default behavior and\n"
" message/write/exec-cmd the users already\n"
" logged in.\n"
" -q, --silent don't message buddies when they log in\n"
" -s, --status-message=STRING use STRING as a template for displaying the\n"
" status of people logging in and out\n"
" -u, --users=LIST adds LIST to the list of buddies for which\n"
" to watch.\n"
" -w, --write-budies be obnoxious and write your buddy every time\n"
" you see him/her log in.\n"
);
}
void bail(char *reason, int exitval){
if(reason) fprintf(stderr, "%s: bailing: %s\n",
g_config.progname, reason);
exit(exitval);
}
syntax highlighted by Code2HTML, v. 0.9.1