terça-feira, 29 de novembro de 2011

Stress test in C !?

This code will create N threads that take B bytes of memory each and spin forever. Call it whihout arguments to see the usage.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define pexit(s) ({perror(s); exit(EXIT_FAILURE);})

static unsigned int sleepi = 0;
void *xmalloc(size_t);
void *tfunction(void *);

int main(int argc, char **argv)
{
        int nthreads;
        int nbytes;
        int i;
        pthread_t *threadv;

        if (argc <= 2) {
                printf("Usage: %s NUMBER_OF_THREADS NUMBER_OF_BYTES_PER_THREAD "
                                "[SLEEP_INTERVAL_IN_SECS]\n", argv[0]);
                exit(EXIT_FAILURE);
        }

        nthreads        = atoi(argv[1]);
        nbytes          = atoi(argv[2]);
        if (argc > 3) {
                sleepi          = atoi(argv[3]);
        }

        threadv = xmalloc(sizeof(pthread_t) * nthreads);
        for (i = 0; i < nthreads; i++) {
               pthread_create(&threadv[i], NULL, tfunction, (void *)&nbytes);
        }
        while (1) sleep(~0lu); /* MAX LONG POSSIBLE */
        return 0;
}

void *xmalloc(size_t siz)
{
        void *n = malloc(siz);
        if (!n)
                pexit("malloc"); 
        return n;
}

void *tfunction(void *num)
{
        int i = *(int *)num;
        while (i--) malloc(1);
        if (sleepi)
                while (1) sleep(sleepi);
        else
                while (1);
}

segunda-feira, 28 de novembro de 2011

Simple socket client

This sample application will connect to addres passed as first argument
and port passed as second argument. Then will send everything received from stdin
to that socket and send everything received as answer from socket to stdout. Simple!
Type quit to exit.

Is useful when you need to rember how to setup PF_INET sockects and when you need something simple
to talk with some socket.

Note: I have used `~' character as prompt.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netdb.h>

#define pexit(s) ({perror(s); exit(EXIT_FAILURE);})

#define BUFLEN 0x400
static char buf[BUFLEN];

int main(int argc, char **argv)
{
    int sock;
    int addr_len;
    int error;
    struct sockaddr_in addr;
    struct hostent *host;

        
    if (argc <= 2) { 
        printf("Usage: %s address port\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock < 0)
        pexit("socket");


    host = gethostbyname(argv[1]);
    if (!host)
        pexit("gethostbyname");

    memcpy(&addr.sin_addr.s_addr, host->h_addr_list[0], sizeof(struct
                sockaddr_in));    
    addr.sin_family = PF_INET;
    addr.sin_port = htons(atoi(argv[2]));

    error = connect(sock, (struct sockaddr *)&addr, sizeof addr);
    if (error)
        pexit("connect");

    for (;;) {
        printf("~ "); /* my prompt */
        fgets(buf, BUFLEN, stdin);    
        if (!strcmp(buf, "quit\n"))
            break;

        error = send(sock, buf, strnlen(buf, BUFLEN), 0);        
        if (error == -1) /* error */
            pexit("send");

        error = recv(sock, buf, BUFLEN, 0);
        if (error == -1)
            pexit("recv");

        printf(buf);
    }    

    close(sock);
    return 0;
}

Testing:


sexta-feira, 11 de novembro de 2011

IP Fail over script writen in perl

This perl script provides an IP Fail Over enviroment, by testing the routes an jumping from one to another if the current route falls out of service.
More information on script comments.


#!/usr/bin/perl  
#
# (c) Daniel Hilst Selli, 2011, <danielhilst@gmail.com>
#
# IP FAIL OVER 
#
# Desc: Provides a IP FAIL OVER environment by testing the current routing and
#       changing it to next route if it fails. "ip" utility is used to change
#       the routes. 
#
# Usage: You need to configure the routes at @routes array. 
# /\_______update_the_comment_______________________/\
#
#       When ping fails on current route, the script searches for a new valid
#       route. That valid route will be the new default route.
#
#       The entry 0 is the standard route. You can request the script to go back
#       to standar route by sending a SIGUSR to it. The standard route will be
#       checked, if is not yet valid the script will not change the current
#       route.
#
#       $dest_host is the url that you will ping. I have setted it to
#       "www.google.com" but you can change it if needed.
#
#       This script goes to background as soon as possible. As a service should
#       do. You can define the log of it on $daemon_log variable. Its default is
#       the name of the script followed by ".log".
#


use strict;
use warnings; 
use Net::Ping; 
use POSIX qw(setsid);
use Time::localtime;
use Fcntl qw(:flock SEEK_END);


#
# START CONFIGURE HERE
#
my @routes = (
        {
#                iface =>  "ppp0",
                source => "200.171.87.72",
                gateway => "dev ppp0",
  init =>  sub {
      print ctime() .  " Rebooting ppp0\n";
      `ifdown ppp0`;
      `ifup ppp0`;
  },
        },
        {
                iface => "eth2",
                source => undef,
                gateway => "via 192.168.5.1",
        },
);
my $dest_host = "www.google.com";
my $standard_route = 1; # Used as index to @routes. So $routes[0] is the 
                        # standard route   
my $daemon_log = $0 . ".log"; 
#
# STOP CONFIGURE HERE
#



#
# Initialization 
# 
my $continue = 1;
my $pid;
my $indx = undef; 
my $current_route = undef;
my $pid_file = "$0.pid";
my $pid_fh = undef;
my $file_lock_fh = undef;
my $file_lock_fname = "$0.lck";
$| = 1; # unbuffered STDOUT

$SIG{TERM} = sub { $continue = 0 };

$SIG{USR1} = sub {
        print ctime() . " Standard route requested\n";
        if ($routes[$standard_route] == $current_route) {
                print ctime() . " the standard route is already".
                " being used, nothing to do\n";
        } elsif ($routes[$standard_route]->{ping}->ping($dest_host)) {
                print ctime() .  " Standard route is valid, ".
                "backing to it\n";
                $indx = $standard_route;
                set_route();
        } else {
                print ctime() . " Standard route offline, nothing to do\n";
        }
};

sub init_routes {
        for my $r (@routes) {
                if ($r->{source}) {
                        $r->{ping} = Net::Ping->new("icmp", 1);
                        $r->{ping}->bind($r->{source});
  } elsif ($r->{iface}) {
      $r->{ping} = Net::Ping->new("icmp", 1, 64, $r->{iface}); 
  } else {
      die "Route without source and iface member\n".
   "You need at least one of them\n";
  }
        } 

        die "\$standard_out setted to out of bounds of ". 
        "\@routes array\n" if $standard_route > $#routes;

        $indx = $standard_route;
        set_route();
}

sub do_flock {
        open($file_lock_fh, ">$file_lock_fname") or die "Can't open lock file".
 " $file_lock_fname";
        unless(flock($file_lock_fh, LOCK_EX | LOCK_NB)) {
                die "Cannot obtain lock. If there is another instance of".
                "this running kill it and try again";    
        }
}

sub do_funlock {
        my ($fname) = @_; 
        unless(flock($file_lock_fh, LOCK_UN)) {
                die "Cannot release lock, this shouldn't be happening";
        }
        close($file_lock_fh);
}

sub set_route {
        $current_route =  $routes[$indx];
        print ctime() . " Changing route to ".
        "$current_route->{gateway}\n";
 
 $current_route->{init}() if $current_route->{init};

        my $error = `ip route del default`; 
        print " Can't delete default route\n" if $error;

        $error = `ip route add default $current_route->{gateway}`;
        die " Can't add default route" if $error;

 print ctime() . " Route changed\n";
}

# Args
# 1 => Ref to global $indx variable
# 2 => The limmit 
sub next_indx {
        my ($indx, $limit) = @_;
        $$indx++;
        if ($$indx > $limit) {
                $$indx = 0;
        }
}


sub on_fail {
        my $error;
        
        print ctime() . " Ping failed\n";
        print ctime() . " Default route is $current_route->{gateway}. Adding new route\n";
        $error = `ip route del default via $current_route->{gateway}`; 
        die $! if $error;

        next_indx(\$indx, $#routes);
        set_route();

        print ctime() . " New route $current_route->{gateway} added\n";
}


sub daemonise {
        umask 0;
        open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
        open STDOUT, ">$daemon_log" or die "Can't write to log: $!";
        open STDERR, ">$daemon_log" or die "Can't write to log: $!";
        defined(my $pid = fork)   or die "Can't fork: $!";
        exit if $pid;
        setsid                    or die "Can't start a new session: $!"; }

#
# MAIN LOOP
#


do_flock();
daemonise();
init_routes();
while ($continue) {
        if ($current_route->{ping}->ping($dest_host)) {
#               print ctime();
#   if ($current_route->{source}) {
#       print " $current_route->{source} live\n";
#   } else {
#       print " $current_route->{iface} live\n";
#   }
                sleep(3);
        } else {
                on_fail();
        }
}
do_funlock();


# vim:ft=perl:tabstop=8:shiftwidth=4:smarttab:noexpandtab:softtabstop=4:ai:tw=80


container_of

/*
 * This example show how get sibling members of a struct. Suppose that you
 * have a struct foo with members A and B. With the macros provided here you can
 * get the address of B, having a pointer to A and knowing that B is the B member
 * of struct foo. This is not my work, is just based on macros provided by gcc
 * compiler __builtin_offsetof() and the container_of() macro found on linux
 * kernel sources. 
 */ 
#include <stdio.h> 

/*
 * You can get the offset of a member on a struct by dereferencing that member on
 * address 0 of such structure.
 */
#define offset_of(type, member) ((unsigned long) &((type *)0)->member)

/*
 * With the capability to get offsets, is possible to get the address of the
 * struct that contains some data. We just need a pointer to that data and the
 * offset of that data on the struct. With this informations we can calculate
 * the address of struct by subtracting the offset from the pointer to that data
 * contained on struct. In the macro above the @ptr is the data contained on
 * struct.
 */
#define container_of(ptr, type, member) \
        ((type *) ((char *)ptr - offset_of(type, member)))

struct foo {
        char *str;
        int len;
};

void print_sibling(int *ip);

int main(void)
{
        struct foo bar = {
                .str = "Hello World",
                .len= 11,
        }; 
        print_sibling(&bar.len);
        
        return 0;

}

/*
 * This function receives an int pointer (@ip) that is known to be the member "len" of
 * a "struct foo". With such information we can do the magic and take any
 * "sibling" member of that struct.
 */
void print_sibling(int *ip)
{
        struct foo *tmp = container_of(ip, struct foo, len);
        puts(tmp->str);
}