Category Archives: Programming

How to send emails from command line

SendEmail is a lightweight command line SMTP agent. It was designed to be easily integrated in bash scripts, Perl programs etc. The program is in fact a Perl script, and it is distributed under GPL.

I’ll start by installing SendEmail, and I will then show how to send the email using a regular gmail.com account or a yahoo.com account. You can send the email to this account, or you can use the account as a relay. The same procedure can be used with most of the online accounts.

Installing SendEmail is easy:

$ sudo apt-get install libio-socket-ssl-perl libnet-ssleay-perl sendemail

Sending an email using gmail.com is as follows:

$ sendemail -f username@gmail.com -t destination@domain.com \
-u “this is the subject of the message” \
-m “and this is the message” \
-s smtp.gmail.com -o tls=yes -xu username -xp password

username and password above are your gmail.com username and password. The message is encrypted using TLS. destination@domain.com is where you want the message to be sent.

Similarly for yahoo.com:

$ sendemail -f username@yahoo.com -t destination@domain.com \
-u “this is the subject of the message” \
-m “and this is the message” \
-s smtp.mail.yahoo.com -xu username -xp password

SendEmail comes with a regular man page and very extensive documentation accessible using –help option. For example:

$ sendemail –help networking

NETWORKING DOCUMENTATION

Networking Options
Options related to networking:
-s SERVER[:PORT]
-b BINDADDR[:PORT]
-o tls=
-o timeout=SECONDS

-s SERVER[:PORT]
This option allows you to specify the SMTP server sendEmail should
connect to to deliver your email message to. If this option is not
specified sendEmail will try to connect to localhost:25 to deliver
the message. THIS IS MOST LIKELY NOT WHAT YOU WANT, AND WILL LIKELY
FAIL unless you have a email server (commonly known as an MTA) running
on your computer!
Typically you will need to specify your company or ISP’s email server.
For example, if you use CableOne you will need to specify:
-s mail.cableone.net
If you have your own email server running on port 300 you would
probably use an option like this:
-s myserver.mydomain.com:300
If you’re a GMail user try:
-s smtp.gmail.com:587 -xu me@gmail.com -xp PASSWD

-b BINDADDR[:PORT]
This option allows you to specify the local IP address (and optional
tcp port number) for sendEmail to bind to when connecting to the remote
SMTP server. This useful for people who need to send an email from a
specific network interface or source address and are running sendEmail on
a firewall or other host with several network interfaces.

-o tls=
This option allows you to specify if TLS (SSL for SMTP) should be enabled
or disabled. The default, auto, will use TLS automatically if your perl
installation has the IO::Socket::SSL and Net::SSLeay modules available,
and if the remote SMTP server supports TLS. To require TLS for message
delivery set this to yes. To disable TLS support set this to no. A debug
level of one or higher will reveal details about the status of TLS.

-o timeout=SECONDS
This option sets the timeout value in seconds used for all network reads,
writes, and a few other things.

$

ezchroot

ezchroot is a small script to chroot into OpenVZ containers. Once inside, you can update or modify the container software. The operation is similar to ezlxc.

#!/bin/bash

if [ $# -gt 0 ]; then
	echo
else
	echo "Usage: ezchroot directory"
	exit 1
fi

cp -L /etc/resolv.conf $1/etc/.
mount -t proc none $1/proc
mount --rbind /dev $1/dev
mount --rbind /sys $1/sys

echo "entering chroot directory"
env NAME=chroot chroot $1 /bin/bash
umount $1/proc
umount $1/dev
umount $1/sys
echo "chroot exited"

A Simple Telnet Client

This is a simple telnet client written in C. In the negotiation phase of the connection it will respond with WONT to any DO coming from the server, and it will encourage the server to DO anything it offers with WILL. The only exception is the screen size setting, advertised as 24 x 80 by the client.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <termios.h>
#include <fcntl.h>

#define DO 0xfd
#define WONT 0xfc
#define WILL 0xfb
#define DONT 0xfe
#define CMD 0xff
#define CMD_ECHO 1
#define CMD_WINDOW_SIZE 31

void negotiate(int sock, unsigned char *buf, int len) {
	int i;
	
	if (buf[1] == DO && buf[2] == CMD_WINDOW_SIZE) {
		unsigned char tmp1[10] = {255, 251, 31};
		if (send(sock, tmp1, 3 , 0) < 0)
			exit(1);
		
		unsigned char tmp2[10] = {255, 250, 31, 0, 80, 0, 24, 255, 240};
		if (send(sock, tmp2, 9, 0) < 0)
			exit(1);
		return;
	}
	
	for (i = 0; i < len; i++) {
		if (buf[i] == DO)
			buf[i] = WONT;
		else if (buf[i] == WILL)
			buf[i] = DO;
	}

	if (send(sock, buf, len , 0) < 0)
		exit(1);
}

static struct termios tin;

static void terminal_set(void) {
	// save terminal configuration
	tcgetattr(STDIN_FILENO, &tin);
	
	static struct termios tlocal;
	memcpy(&tlocal, &tin, sizeof(tin));
	cfmakeraw(&tlocal);
	tcsetattr(STDIN_FILENO,TCSANOW,&tlocal);
}

static void terminal_reset(void) {
	// restore terminal upon exit
	tcsetattr(STDIN_FILENO,TCSANOW,&tin);
}

#define BUFLEN 20
int main(int argc , char *argv[]) {
	int sock;
	struct sockaddr_in server;
	unsigned char buf[BUFLEN + 1];
	int len;
	int i;

	if (argc != 2) {
		printf("Usage: %s address [port]\n", argv[0]);
		return 1;
	}
	int port = 23;
	if (argc == 3)
		port = atoi(argv[2]);

	//Create socket
	sock = socket(AF_INET , SOCK_STREAM , 0);
	if (sock == -1) {
		perror("Could not create socket. Error");
		return 1;
	}

	server.sin_addr.s_addr = inet_addr(argv[1]);
	server.sin_family = AF_INET;
	server.sin_port = htons(23);

	//Connect to remote server
	if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) {
		perror("connect failed. Error");
		return 1;
	}
	puts("Connected...\n");

	// set terminal
	terminal_set();
	atexit(terminal_reset);
	
	struct timeval ts;
	ts.tv_sec = 1; // 1 second
	ts.tv_usec = 0;

	while (1) {
		// select setup
		fd_set fds;
		FD_ZERO(&fds);
		if (sock != 0)
			FD_SET(sock, &fds);
		FD_SET(0, &fds);

		// wait for data
		int nready = select(sock + 1, &fds, (fd_set *) 0, (fd_set *) 0, &ts);
		if (nready < 0) {
			perror("select. Error");
			return 1;
		}
		else if (nready == 0) {
			ts.tv_sec = 1; // 1 second
			ts.tv_usec = 0;
		}
		else if (sock != 0 && FD_ISSET(sock, &fds)) {
			// start by reading a single byte
			int rv;
			if ((rv = recv(sock , buf , 1 , 0)) < 0)
				return 1;
			else if (rv == 0) {
				printf("Connection closed by the remote end\n\r");
				return 0;
			}

			if (buf[0] == CMD) {
				// read 2 more bytes
				len = recv(sock , buf + 1 , 2 , 0);
				if (len  < 0)
					return 1;
				else if (len == 0) {
					printf("Connection closed by the remote end\n\r");
					return 0;
				}
				negotiate(sock, buf, 3);
			}
			else {
				len = 1;
				buf[len] = '\0';
				printf("%s", buf);
				fflush(0);
			}
		}
		
		else if (FD_ISSET(0, &fds)) {
			buf[0] = getc(stdin); //fgets(buf, 1, stdin);
			if (send(sock, buf, 1, 0) < 0)
				return 1;
			if (buf[0] == '\n') // with the terminal in raw mode we need to force a LF
				putchar('\r');
		}
	}
	close(sock);
	return 0;
}

bash Cheat Sheet

Test grep result

grep "somestring" somefile
if [ "$?" -eq 0 ];
then
	echo "processing somefile"
fi

Test environment variable

if [ -n "${ENV_VAR+x}" ]
then
	... do something ...
else
	... do something else ...
fi

Test if a program is installed

if which program >/dev/null; then
    echo program found
else
    echo program not found
    exit 1
fi

C Cheat Sheet

Array initialization

int a[4] = {[2] = 6, [3] = 7};
int grid[100][100] = [0][0] = 8, [50][25] = 7};

Structure initialization

struct address {
                 int street_no;
                 char *street_name;
                 char *city;
                 char *prov;
                 char *postal_code;
};
struct address temp_address = { .city = "Hamilton", .prov = "Ontario" };

struct a {
         struct b {
              int c;
              int d;
          } e;
         float f;
} g = {.e.c = 3 };

Union initialization

union {
      char birthday[9];
      int age;
      float weight;
} people = { .age = 14 };

printf format

%[flags][min field width][precision][length]conversion specifier

where:

flags:   
   #	Alternate
   0	zero pad
   -	left align
   +	explicit + - sign
     	space for + sign
   '	locale thousands grouping
   I	Use locale's alt digits  

min field width: #,*

precision: .#, .*

length:
   hh	char
   h	short
   l	long
   ll	long long
   j	[u]intmax_t
   z	size_t
   t	ptrdiff_t
   L	long double

conversion specifier:
   c	unsigned char
   d	signed int
   u	unsigned int
   x	unsigned hex int
   X	unsigned HEX int
   e	[-]d.ddde±dd double,
   E	[-]d.dddE±dd double

Examples:

printf("%08X", var);		00001234
printf("%20s","string");	string (right aligned)
printf("%*s", 20, "string");	string (right aligned, the alignment is specified as an argument)
printf("%-20s", "string");	string (left aligned)
printf("%-20.20s", "string");	string (the string is truncated if it is too long)

Easy LXC: Running OpenVZ containers in LXC

ezlxc (Easy LXC) is a small Bash script for running virtual machines (VM) using Linux Containers (LXC). The VM containers are based on the templates provided by OpenVZ project.

In an LXC environment, a single Linux kernel is shared between the host and the virtual machines. Only the essential needed services are run in VMs. The VM is basically a chroot-based environment with the added network/process separation provided by LXC virtualization. Memory requirements for this type of setup is very low – my old dual-core AMD computer with 1GB of RAM memory runs easily 10 VMs.

ezlxc is based on ssh-template script developed and distributed with LXC. I have tested it on a Fedora 17 x86_64 computer and it will probably work without modifications on any recent Linux distribution. Copy the script below in a text editor and save it as ezlxc. Make the file executable (chmod +x ezlxc) and copy it in /usr/local/bin/directory. The copying is performed as root, in fact all the operations below can only be performed as root.

#!/bin/bash

echo "1" > /proc/sys/net/ipv4/ip_forward
brctl addbr br0
ifconfig br0 up
brctl addbr br1
ifconfig br1 up

generate_rclocal() {

cat <<EOF > etc/rc.local
echo "rc.local running..."
mount proc /proc -t proc
mount tmpfs /dev/shm -t tmpfs -o size=16m
mount devpts /dev/pts -t devpts
mount -t sysfs sys sys/

#****************************************
# initialize the rest of the system here
#****************************************

#/bin/echo "*   default gateway..."
#/sbin/route add default gw 10.0.0.1

echo "*   starting  crond..."
/usr/sbin/crond -n &

echo "*   starting  rsyslog..."
/sbin/rsyslogd -c 5

echo "*   starting sshd..."
/usr/sbin/sshd

echo "*   starting control session..."
/bin/bash
echo "control session exited, VM shutting down..."

#****************************************
# shutdown all processes here
#****************************************
pkill crond
pkill rsyslogd
pkill sshd
umount /sys
umount /dev/shm
umount /dev/pts
umount /proc

EOF

}

create_fs() {
	if [ ! -f etc/rc.local ]
	then
		echo "creating a default /etc/rc.local, please modify it!"
		generate_rclocal
		chmod +x etc/rc.local
	fi
	
	if [ ! -f etc/ssh/ssh_host_rsa_key ]
	then
		echo "configuring sshd"
		ssh-keygen -t rsa -f etc/ssh/ssh_host_rsa_key -N ""
		ssh-keygen -t dsa -f etc/ssh/ssh_host_dsa_key -N ""
	fi
	
	
	echo "populating dev directory"
	mknod -m 666 dev/null c 1 3
	mknod -m 666 dev/zero c 1 5
	mknod -m 666 dev/random c 1 8
	mknod -m 666 dev/urandom c 1 9
	mkdir -m 755 dev/pts
	mkdir -m 1777 dev/shm
	mknod -m 666 dev/tty c 5 0
	mknod -m 600 dev/console c 5 1
	mknod -m 666 dev/tty0 c 4 0
	mknod -m 666 dev/full c 1 7
	mknod -m 600 dev/initctl p
	mknod -m 666 dev/ptmx c 5 2
}

do_exec() {
	echo "chrooting to $1"
	chroot $1 /bin/bash -i /etc/rc.local
	sleep 2
	
	echo "VM exited"
}




if [ $# -gt 0 ]; then
	echo
else
	echo "Usage: ezlxc name"
	echo "where"
	echo "	name: virtual machine directory"
	exit 1
fi

case "$1" in
lxc-exec)
	do_exec $2
	;;
	
*)
	# directory setup
	DIRNAME=`dirname $1`
	VMNAME=`basename $1`
	echo "switching to $1 directory"
	cd $1
	create_fs
	cd -
	
	# create/start/destroy virtual machine
	echo "creating virtual machine"
	lxc-create -n $VMNAME
	echo "starting virtual machine"
	lxc-execute -n $VMNAME -f $1/lxc.conf $0 lxc-exec $1
	echo "destroying virtual machine"
	lxc-destroy -n $VMNAME
	mount -t proc proc /proc
	;;
esac

The steps involved in building and running a container are described below.

1. Download fedora-17-x86_64 template from OpenVZ OS template page.

2. Create a new directory for the container and unpack the template.

# cd ~
# mkdir vm1
# cd vm1
# tar -xzvj fedora-17-x86_64.tar.gz

3. In vm1 directory, create a lxc.conf file with the following content:

lxc.utsname = vm1
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 4a:49:43:49:a0:11
lxc.network.ipv4 = 10.0.0.10/24

This is the configuration file for the container.

  • vm1 is the name of the container;
  • the container has a network interface with the specified MAC and IP addresses;
  • the network interface is connected to a bridge interface br0 on the host.

4. Start the container:

# cd ~
# ezlxc vm1
switching to vm1 directory
creating a default /etc/rc.local, please modify it!
populating dev directory
creating virtual machine
‘vm1' created
starting virtual machine
chrooting to vm1
rc.local running...
*   starting  crond...
*   starting  rsyslog...
*   starting sshd...
*   starting control session...
[root@vm1 /]#

In this moment, the current terminal is logged into the new virtual machine. This is the controlling terminal. As long as this terminal is active, the virtual machine is running. To stop it, just type exit or close the terminal window.

By default, the virtual machine runs three processes: crnod (UNIX job scheduler), rsyslog (system logger) and sshd (OpenSSH server). The processes are started from /etc/rc.local file inside the container (~/vm1/etc/rc.local).

# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  14788   572 pts/0    S    23:23   0:00 /usr/lib64/lxc/lxc-init --
root         2  0.0  0.1 112092  1248 pts/0    S    23:23   0:00 /bin/bash /usr/local/bin/ezlxc
root         7  0.0  0.2 114288  1804 pts/0    S    23:23   0:00 /bin/bash -i /etc/rc.local
root        20  0.0  0.1 118392  1400 pts/0    S    23:23   0:00 /usr/sbin/crond -n
root        22  0.0  0.3 177796  3432 ?        Sl   23:23   0:00 /sbin/rsyslogd -c 5
root        26  0.0  0.1  77604  1244 ?        Ss   23:23   0:00 /usr/sbin/sshd
root        27  0.0  0.2 114292  1960 pts/0    S    23:23   0:00 /bin/bash
root        38  0.0  0.1 115728  1164 pts/0    R+   23:28   0:00 ps aux

You can open and modify /etc/rc.local and start more processes. Also, in this file you can set various other parameters such as the default gateway. A typical rc.local file generated by ezlxc looks like this:

echo “rc.local running..."
mount proc /proc -t proc
mount tmpfs /dev/shm -t tmpfs -o size=16m
mount devpts /dev/pts -t devpts

#****************************************
# initialize the rest of the system here
#****************************************

#/bin/echo “*   default gateway..."
#/sbin/route add default gw 10.0.0.1

echo “*   starting  crond..."
/usr/sbin/crond -n &

echo “*   starting  rsyslog..."
/sbin/rsyslogd -c 5

echo “*   starting sshd..."
/usr/sbin/sshd

echo “*   starting control session..."
/bin/bash
echo “control session exited, VM shutting down...”

#****************************************
# shutdown all processes here
#****************************************
pkill crond
pkill rsyslogd
pkill sshd
umount /dev/shm
umount /dev/pts
umount /proc

More servers and applications can be installed in VM (yum install …) and started in rc.local.

5. The IP address of the VM was set to 10.0.0.10. We need to set an IP address on bridge br0 on the host also. Open a new terminal and configure br0 interface:

# ifconfig br0 10.0.0.1

In this moment we can ping the virtual machine from the host

# ping 10.0.0.10
PING 10.0.0.10 (10.0.0.10) 56(84) bytes of data.
64 bytes from 10.0.0.10: icmp_req=1 ttl=64 time=0.196 ms
64 bytes from 10.0.0.10: icmp_req=2 ttl=64 time=0.113 ms
64 bytes from 10.0.0.10: icmp_req=3 ttl=64 time=0.115 ms
^C
--- 10.0.0.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.113/0.141/0.196/0.039 ms

6. Back in the controlling terminal, we set a root password. This allows us to access the VM using SSH:

[root@vm1 /]# passwd root
Changing password for user root.
New password: 
Retype new password: 
passwd: all authentication tokens updated successfully.
[root@vm1 /]#

From the previous host terminal we can SSH into the VM:

# ssh root@10.0.0.10
root@10.0.0.10‘s password: 
Last login: Wed Oct 24 23:44:57 2012 from 10.0.0.1
[root@vm1 ~]#

7. To stop the VM type exit in the controlling terminal:

[root@vm1 /]# exit
exit
control session exited, VM shutting down...
VM exited
destroying virtual machine