I made a Linux util to send messages to users logged in to the same workstation as you.

Background info that you don’t have to read:
At our state university, we have a few computer labs for cs students that run Fedora. At any given moment, there could be multiple users logged on to one of the same computers in the lab either physically or remotely. Using the write command, you can send messages to other users logged in to the same workstation through their terminal (use wall command to send msg to all users). Unfortunately, the write command informs the reciever of the sender’s username. I took a look at the source code of the write command and found out that all it does is use the functions from utmp.h to read info about logged in users and then it writes to one of the psuedo-terminal files in /dev/pts directory to send a message to a user’s terminal. So I made my own variation of the write command.

Help message:

Usage: ttymsg user
Sends a message to the specified user through their terminal.
If user is @ then the message is sent to all users logged in on this system.
The message to send is specified through standard input.

If no arguments are given, this command lists all logged in users.

ttymsg.c

#include "ttyuser.h"
#include <stdio.h>
#include <stdlib.h>


void list_users();
void msg_user(const char *user);

void help(const char *program) {
    printf(
        "Usage: %s user\n"
        "Sends a message to the specified user through their terminal.\n"
        "If user is @ then the message is sent to all users logged in on this system.\n"
        "The message to send is specified through standard input.\n\n"
        "If no argument is given, this command lists all logged in users.\n",
        program
    );
    exit(0);
}

int main(int argc, const char **argv) {
    if (argc == 2) {
        if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))
            help(argv[0]);
        else msg_user(argv[1]);
    } else list_users();
    return 0;
}

void list_users() {
    FILE *f;
    struct ttyuser *t;
    f = fopen(UTMP_FILE, "r");
    if (f == 0) {
        fprintf(stderr, "Failed to open %s\n", UTMP_FILE);
        exit(1);
    }
    printf(
        "%-20s %-20s %-20s %-20s\n",
        "Name",
        "TTY",
        "Hostname",
        "IP Address"
    );
    for (t = readudb(f); t != 0; t = readudb(f)) {
        printf(
            "%-20s %-20s %-20s %-20s\n",
            t->tu_user,
            t->tu_ttyname,
            t->tu_host,
            t->tu_addr
        );
    }
    fclose(f);
}

void msg_user(const char *user) {
    int chr;
    FILE *f, *tty;
    struct ttyuser *t;
    char *buf;
    size_t i = 256, offset = 0;
    buf = (char*)malloc(i);
    if (buf == 0) exit(1);
    f = fopen(UTMP_FILE, "r");
    if (f == 0) {
        free(buf);
        fprintf(stderr, "Failed to open %s\n", UTMP_FILE);
        exit(1);
    }
    for (chr = getchar(); chr != EOF; chr = getchar()) {
        if (offset == i) {
            buf = (char*)realloc(buf, i *= 2);
            if (buf == 0) {
                fclose(f);
                exit(1);
            }
        }
        buf[offset++] = chr;
    }
    for (t = readudb(f); t != 0; t = readudb(f)) {
        if ((user[0] == '@' && user[1] == 0) == 0) {
            if (strcmp(t->tu_user, user)) continue;
        }
        tty = fopen(t->tu_ttyname, "w");
        if (tty == 0) continue;
        fputs(buf, tty);
        fclose(tty);
    }
    free(buf);
    fclose(f);
}
 

ttyuser.h

#ifndef TTYUSER_H
#define TTYUSER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <utmp.h>

struct ttyuser {
    char tu_ttyname[UT_LINESIZE + 8];
    char tu_user[UT_NAMESIZE];
    char tu_host[UT_HOSTSIZE];
    char tu_addr[INET_ADDRSTRLEN];
    char tu_addr6[INET6_ADDRSTRLEN];
};

#ifndef UTMP_FILE
#define UTMP_FILE "/var/run/utmp"
#endif

#ifndef WTMP_FILE
#define WTMP_FILE "/var/log/wtmp"
#endif

struct ttyuser *readudb(FILE *u);
int readudb_r(FILE *u, struct ttyuser *entry, struct ttyuser **result);

#ifdef __cplusplus
}
#endif

#endif
 

ttyuser.c

#include "ttyuser.h"

struct ttyuser *readudb(FILE *up) {
    static struct ttyuser t;
    struct utmp u;
    for (;;) {
        if (fread(&u, sizeof(u), 1, up) == 0) return 0;
        if (u.ut_type != USER_PROCESS) continue;
        if (u.ut_line[0] == ':') continue;
        if (strncmp(u.ut_line, "pts/", 4)) continue;

        strcpy(t.tu_ttyname, "/dev/");
        strcat(t.tu_ttyname, u.ut_line);
        strcpy(t.tu_user, u.ut_user);
        strcpy(t.tu_host, u.ut_host);
        inet_ntop(AF_INET, &u.ut_addr_v6[0], t.tu_addr, INET_ADDRSTRLEN);
        inet_ntop(AF_INET6, u.ut_addr_v6, t.tu_addr6, INET6_ADDRSTRLEN);
        break;
    }
    return &t;
}

int readudb_r(FILE *up, struct ttyuser *t, struct ttyuser **result) {
    struct utmp u;
    *result = t;
    for (;;) {
        if (fread(&u, sizeof(u), 1, up) == 0) { *result = 0; return 0; }
        if (u.ut_type != USER_PROCESS) continue;
        if (u.ut_line[0] == ':') continue;

        strcpy(t->tu_ttyname, "/dev/");
        strcat(t->tu_ttyname, u.ut_line);
        strcpy(t->tu_user, u.ut_user);
        strcpy(t->tu_host, u.ut_host);
        inet_ntop(AF_INET, &u.ut_addr_v6[0], t->tu_addr, INET_ADDRSTRLEN);
        inet_ntop(AF_INET6, u.ut_addr_v6, t->tu_addr6, INET6_ADDRSTRLEN);
        break;
    }
    return 0;
}
 

One thought on “

  1. Well done . I suppose the next step is how to use openssl/evp.h with this source code. Your color of text “Notify me …” is same and text is not show up . Have a great day!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: