Livro sobre C e Linux

Hoje decidi abrir uma exceção e postar esse link aqui. É um livro que escrevi há 9 anos e nunca foi revisado nem publicado, mas que ainda pode ser útil para muitos novos programadores.

 

Anúncios

urban terror, udp proxy

A internet é mesmo um lugar maluco. Eu não sou muito de jogar, mas nos últimos meses um amigo me mostrou o urban terror, um jogo no estilo counter strike que é gratuito, baseado na engine do quake3 arena.

Dias depois fui banido de um dos servidores mais populares do negócio, o bashfull, e então desencanei e nunca mais joguei lá. Porém, várias vezes que fui jogar, não havia ninguém nos outros servidores, e lá estava sempre cheio de gente.

ut, banned

Tentei o meio normal da coisa e mandei 2 emails pro admin, nos últimos 20 dias. Como fui ignorado, e meu IP do virtua é fixo, não sobrou outra alternativa a não ser usar um proxy no estilo BNC, tipo os que eu usava a uns 10 anos atrás nas épocas do irc.

Pra terminar, não achei nada que preste pra fazer proxy Nx1 via udp – talvez eu seja burro de mais pra procurar essas bobeiras – e então acabei escrevendo um em python.

Em 20 minutos, fiz tudo e testei em um ubuntu. Ficou bem simples, veja ai:

#!/usr/bin/env python
# coding: utf-8
# 20081225 AF
#
# urban terror asynchronous udp proxy
# requires python 2.5 (linux, bsd, etc)
# configuration
maxclients = 10
proxyport = 27960
ut_server_addr = ‘189.38.63.196’
ut_server_port = 27962

# don’t touch anything below.
import os, sys, time, syslog, socket, select, threading

clients = {}
timeout = 15
pktsize = 4096
infolog = lambda msg: syslog.syslog(syslog.LOG_INFO, msg)

def daemonize():
    # double fork, ignore exceptions
    pid = os.fork()
    if not pid:
        os.setsid()
        pid = os.fork()
        if not pid:
            os.chdir(‘/’)
            [k.close() for k in sys.stdin, sys.stdout, sys.stderr]
        else:
            os._exit(0)
    else:
        os._exit(0)

def cleaner():
    # remove inactive slots
    while 1:
        now = time.time()
        for slot in clients.values():
            if now – slot.time > timeout:
                infolog(‘client %s disconnected’ % str(slot.addr))
                del clients[slot.addr]
        time.sleep(5.0)

class proxy:
    def __init__(self, addr):
        self.addr = addr
        self.time = time.time()
        self.fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.fd.connect((ut_server_addr, ut_server_port))

    def send(self, chunk):
        self.time = time.time()
        return self.fd.send(chunk)

if __name__ == ‘__main__’:
    fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    fd.bind((”, proxyport))

    daemonize()
    syslog.openlog(‘utproxy’, syslog.LOG_PID, syslog.LOG_DAEMON)
    threading.Thread(target=cleaner).start()

    while 1:
        current = [k.fd for k in clients.values()] + [fd]
        rr, rw, er = select.select(current, [], [])

        if fd in rr:
            chunk, addr = fd.recvfrom(pktsize)
            if addr in clients.keys(): # from proxy (cli=addr) to server
                clients[addr].send(chunk)
            else:
                if len(clients) < maxclients:                     infolog('client %s connected' % str(addr))                     clients[addr] = proxy(addr)                     clients[addr].send(chunk)                 else:                     syslog.syslog(syslog.LOG_INFO,                         'client %s rejected: proxy is full' % str(addr))         for cli in clients.values():             if cli.fd in rr: # from server to proxy (cli=addr)                 chunk = cli.fd.recv(pktsize)                 fd.sendto(chunk, cli.addr)[/sourcecode] Como o proxy não precisa fazer absolutamente nenhum processamento, todo o repasse de pacotes é feito de modo assíncrono, em um único processo, e uma thread verifica as conexões inativas para removê-las da lista. Até poderia fazer o proxy de outras maneiras, talvez usando asynchat ou mesmo twisted, mas acabei optando pelo modo tradicional e o resultado é que ele não depende de nenhum módulo externo, apenas coisas básicas do python 2.5.

ut, favorite

Agora, quando entrar lá, só vou matar o bashfull. Meu nick é /dev/null.


linux fmemopen, bsd funopen

Enquanto desenvolvia um sistema, me deparei com um problema entre plataformas, relacionado ao uso e manipulação de arquivos temporários em memória.

Ao invés de gravar um arquivo temporário no disco e ir populando com o conteúdo, meu caso exigia que esse arquivo fosse populado em memória para uso posterior, e a API do Linux (via libc) fornece a função fmemopen(3).

Porém, meu ambiente de desenvolvimento é no Mac OS X, e devido ao tipo do software, era inevitátel tanto desenvolver como testar tudo nessa mesma máquina, para então posteriormente colocá-lo em produção em um Linux. O problema é que o OS X herdou a API do BSD, que ao invés de fmemopen, fornece apenas o funopen(3).

Por esse motivo, acabei escrevendo uma versão bem simples do fmemopen do Linux para usar no OS X, usando funopen internamente. O único detalhe, e provavelmente a diferença entre a versão original do fmemopen pra minha, é que a minha simplesmente ignora o modo de acesso ao arquivo (“r”, “w”, etc). O fato é que agora posso continuar desenvolvendo tudo no OS X e depois compilar no Linux obtendo o mesmo resultado.

fmem.h:

/*
 * fmem.h : fmemopen() on top of BSD's funopen()
 * 20081017 AF
 */

#ifndef _FMEM_H
#define _FMEM_H

#ifndef linux
#include <stdio.h>
extern FILE *fmemopen(void *buf, size_t size, const char *mode);
#else
#define _GNU_SOURCE
#endif

#endif /* fmem.h */

fmem.c:

/*
 * fmem.c : fmemopen() on top of BSD's funopen()
 * 20081017 AF
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef linux
struct fmem {
    size_t pos;
    size_t size;
    char *buffer;
};
typedef struct fmem fmem_t;

static int readfn(void *handler, char *buf, int size)
{
    int count = 0;
    fmem_t *mem = handler;
    size_t available = mem->size - mem->pos;

    if(size > available) size = available;
    for(count=0; count < size; mem->pos++, count++)
        buf[count] = mem->buffer[mem->pos];

    return count;
}

static int writefn(void *handler, const char *buf, int size)
{
    int count = 0;
    fmem_t *mem = handler;
    size_t available = mem->size - mem->pos;

    if(size > available) size = available;
    for(count=0; count < size; mem->pos++, count++)
        mem->buffer[mem->pos] = buf[count];

    return count; // ? count : size;
}

static fpos_t seekfn(void *handler, fpos_t offset, int whence)
{
    size_t pos;
    fmem_t *mem = handler;

    switch(whence) {
        case SEEK_SET: pos = offset; break;
        case SEEK_CUR: pos = mem->pos + offset; break;
        case SEEK_END: pos = mem->size + offset; break;
        default: return -1;
    }

    if(pos < 0 || pos > mem->size) return -1;

    mem->pos = pos;
    return (fpos_t) pos;
}

static int closefn(void *handler)
{
    free(handler);
    return 0;
}

/* simple, but portable version of fmemopen for OS X / BSD */
FILE *fmemopen(void *buf, size_t size, const char *mode)
{
    fmem_t *mem = (fmem_t *) malloc(sizeof(fmem_t));

    memset(mem, 0, sizeof(fmem_t));
    mem->size = size, mem->buffer = buf;
    return funopen(mem, readfn, writefn, seekfn, closefn);
}
#endif

E finalmente, um programa simples para testar o uso da função, que compila e produz o exato mesmo resultado tanto no Linux como no OS X – não testei em outros BSDs, mas suponho que deva funcionar também.

test.c:

/*
 * test.c : memory fp test
 * 20081212 AF
 */

#include "fmem.h"
#include <stdio.h>
#include <string.h>

int main()
{
    FILE *fp;
    char buff[128], temp[128];

    memset(buff, 0, sizeof buff);

    fp = fmemopen(buff, sizeof buff, "r+");
    fprintf(fp, "hello world");
    fseek(fp, 0, SEEK_SET);

    memset(temp, 0, sizeof buff);
    fgets(temp, sizeof temp, fp);

    fclose(fp);

    fprintf(stdout, "read: [%s]\n", temp);

    return 0;
}

Para compilar, basta o usar o seguinte Makefile:

CC=gcc
RM=rm -f
NAME=test

.SUFFIXES = .o
OBJECTS = test.o fmem.o

.c.o:
    $(CC) -Wall -c -o $@ $< all: $(OBJECTS)     $(CC) -Wall -o $(NAME) $(OBJECTS) clean:     $(RM) $(NAME) $(OBJECTS)[/sourcecode]


levelhead: o jogo mais 3d !

Sem palavras. Veja o video, acesse o site, compile o fonte.


mac address da placa de rede com python (linux)

Pra pegar o endereço MAC (Media Access Control) de uma placa de rede com python, você precisa primeiro ser root.

Aqui vai um exemplo:

sudo python
>>> import socket
>>> fd = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
>>> fd.bind(('eth0', 9999))
>>> mac = fd.getsockname()[-1]
>>> print ':'.join(['%02x' % ord(n) for n in mac])
00:16:d3:93:c2:e6

Outros sistemas:

1. Encontrei um cara que faz isso no Windows da maneira mais tosca – interpretando o resultado do ipconfig /all. Acho que deve ter uma maneira melhor que essa. Em todo caso, veja o link.

2. Outros Unix-like tipo BSD não têm AF_PACKET, então isso não funciona. Pra cada um, tem que saber qual é o ioctl que manda pro socket, ai usando o módulo struct do python fica fácil pegar o resultado.


usando find para encontrar arquivos antigos

A maioria dos administradores de sistemas UNIX provavelmente já fez isso. Se não, ainda vai precisar.
É uma coisa simples, que todo mundo sabe que é possível, mas sempre esquece. Eu mesmo esqueço e por isso vou postar aqui, ai fica mais fácil quando esquecer denovo.

find /tmp -iname \*.jpg -atime +1 -print

Esse comando mostra os arquivos com extensão JPG (case insensitive, -iname) no diretório /tmp, que tenham sido acessados a mais de 1 dia – por isso +N. Pode-se usar -N para datas menores que N: se fosse -1, encontraria arquivos que foram acessados a menos de 1 dia. Quando não há sinal, encontra com exatidão: se fosse -atime 2, encontraria arquivos que foram acessados a exatamente 2 dias.

No manual find(1) há a descrição de todas opções, como -atime, -amin e -anewer. Também, das variantes com -ctime e -mtime (-cmin, etc).

Só pra lembrar:

  • Estes dados são armazenados na struct stat, descrita no manual stat(2)
  • o atime é atualizado quando há algum tipo de acesso no arquivo – com execve(2), read(2), etc.
  • o mtime é atualizado quando há modificação no arquivo – quando novos dados são escritos nele, truncate(2), write(2). No caso de diretórios, o mtime do diretório é atualizado quando arquivos são criados ou removidos nele.
  • o ctime é atualizado quando há escrita no arquivo, ou quando suas permissões são alteradas – write(2), chmod(2), chown(2), etc.

ping: partimage is not ghost

Um sistema ideal para backup e restore offline, que cria imagens de partições do disco. Soa como o ghost, né? Mas, além de várias vantagens sobre o ghost, o PING é totalmente gratuito e distribuído como ISO – que ao gravar em um CD pode ser usado como live-cd para executar as tarefas.

Além disso, as imagens geradas pelo PING podem ser gravadas em CD, DVD, já com sistema de restore automatizado.

Para mais informações, veja o site do PING.