Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Wednesday, April 25, 2012

glib/GIO Sockets and Binding on IPv4 and IPv6

Today I learned that it isn't possible to bind to the same port on both IPv4 and IPv6 in GIO:

#include <glib.h> #include <gio/gio.h> #include <stdlib.h> #include <stdio.h> int main(int argc, const char ** argv) { char * valid; unsigned long int port; GError * err = NULL; GSocketFamily gfamily = G_SOCKET_FAMILY_IPV4; GSocket * sockets[2]; GInetAddress * giaddress; GSocketAddress * gsaddress; int i; g_type_init(); if(argc == 1) { fprintf(stderr, "Usage: %s port\n", argv[0]); return 1; } port = strtoul(argv[1], &valid, 10); if(port == 0 || port > 0xFFFF || *valid != 0) { fprintf(stderr, "Invalid port: %s\n", argv[1]); return 1; } while(gfamily != G_SOCKET_FAMILY_INVALID) { i = (gfamily == G_SOCKET_FAMILY_IPV4) ? 0 : 1; printf("Family: %d, port: %lu\n", gfamily, port); sockets[i] = g_socket_new(gfamily, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &err); if(sockets[i] == NULL || err != NULL) { fprintf(stderr, "Failed to create server socket: %s\n", err->message); g_error_free(err); if(sockets[i] != NULL) g_object_unref(sockets[i]); return 2; } giaddress = g_inet_address_new_any(gfamily); gsaddress = g_inet_socket_address_new(giaddress, port); if(!g_socket_bind(sockets[i], gsaddress, TRUE, &err) || err != NULL) { fprintf(stderr, "Failed to bind to port: %s\n", err->message); g_error_free(err); if(sockets[i] != NULL) g_object_unref(sockets[i]); if(gsaddress != NULL) g_object_unref(gsaddress); return 2; } if(!g_socket_listen(sockets[i], &err) || err != NULL) { fprintf(stderr, "Failed to listen on socket: %s\n", err->message); g_error_free(err); if(sockets[i] != NULL) g_object_unref(sockets[i]); if(gsaddress != NULL) g_object_unref(gsaddress); return 2; } gfamily = (gfamily == G_SOCKET_FAMILY_IPV4) ? G_SOCKET_FAMILY_IPV6 : G_SOCKET_FAMILY_INVALID; } for(i = 0; i < 2; i++) g_object_unref(sockets[i]); return 0; }

Typical output:

% ./gfail 3000 Family: 2, port: 3000 Family: 10, port: 3000 Failed to bind to port: Error binding to address: Address already in use

Weird.

Thursday, March 8, 2012

g_strdup and g_free Corruption

I just looked further into it, and realized my actual mistake below. Whoops!

I found an interesting little nit when messing around while researching glib's behavior:

#include <glib.h> #include <string.h> int main(int argc, char ** argv) { gchar * foo; foo = g_strdup("100"); g_free(foo); g_printf("FOO: %p\n", foo); gchar * bar = g_strdup(foo); g_printf("BAR: %p = '%s'\n", bar, bar); g_free(bar); if(foo == bar) g_printf("FOO == BAR\n"); g_free(foo); return 0; }% ./gnfree2 FOO: 0x16ad010 BAR: 0x16ad010 = '' FOO == BAR *** glibc detected *** ./gnfree2: double free or corruption (fasttop): 0x00000000016ad010 *** ======= Backtrace: ========= /lib/libc.so.6(+0x78e66)[0x7fa8a16f9e66] ./gnfree2[0x40073e] /lib/libc.so.6(__libc_start_main+0xed)[0x7fa8a16a238d] ./gnfree2[0x4005e9] ======= Memory map: ======== 00400000-00401000 r-xp 00000000 08:04 418428329 /home/nehodges/Programs/gnfree2 00600000-00601000 rw-p 00000000 08:04 418428329 /home/nehodges/Programs/gnfree2 016ad000-016ce000 rw-p 00000000 00:00 0 [heap] 7fa8a0ded000-7fa8a0e02000 r-xp 00000000 08:03 131410 /usr/lib/libgcc_s.so.1 7fa8a0e02000-7fa8a1002000 ---p 00015000 08:03 131410 /usr/lib/libgcc_s.so.1 7fa8a1002000-7fa8a1003000 rw-p 00015000 08:03 131410 /usr/lib/libgcc_s.so.1 7fa8a1003000-7fa8a101a000 r-xp 00000000 08:03 2359314 /lib/libpthread-2.15.so 7fa8a101a000-7fa8a1219000 ---p 00017000 08:03 2359314 /lib/libpthread-2.15.so 7fa8a1219000-7fa8a121a000 r--p 00016000 08:03 2359314 /lib/libpthread-2.15.so 7fa8a121a000-7fa8a121b000 rw-p 00017000 08:03 2359314 /lib/libpthread-2.15.so 7fa8a121b000-7fa8a121f000 rw-p 00000000 00:00 0 7fa8a121f000-7fa8a1226000 r-xp 00000000 08:03 2359337 /lib/librt-2.15.so 7fa8a1226000-7fa8a1425000 ---p 00007000 08:03 2359337 /lib/librt-2.15.so 7fa8a1425000-7fa8a1426000 r--p 00006000 08:03 2359337 /lib/librt-2.15.so 7fa8a1426000-7fa8a1427000 rw-p 00007000 08:03 2359337 /lib/librt-2.15.so 7fa8a1427000-7fa8a1480000 r-xp 00000000 08:03 135459 /usr/lib/libpcre.so.1.0.0 7fa8a1480000-7fa8a167f000 ---p 00059000 08:03 135459 /usr/lib/libpcre.so.1.0.0 7fa8a167f000-7fa8a1680000 r--p 00058000 08:03 135459 /usr/lib/libpcre.so.1.0.0 7fa8a1680000-7fa8a1681000 rw-p 00059000 08:03 135459 /usr/lib/libpcre.so.1.0.0 7fa8a1681000-7fa8a1818000 r-xp 00000000 08:03 2359610 /lib/libc-2.15.so 7fa8a1818000-7fa8a1a18000 ---p 00197000 08:03 2359610 /lib/libc-2.15.so 7fa8a1a18000-7fa8a1a1c000 r--p 00197000 08:03 2359610 /lib/libc-2.15.so 7fa8a1a1c000-7fa8a1a1e000 rw-p 0019b000 08:03 2359610 /lib/libc-2.15.so 7fa8a1a1e000-7fa8a1a22000 rw-p 00000000 00:00 0 7fa8a1a22000-7fa8a1b15000 r-xp 00000000 08:03 135544 /usr/lib/libglib-2.0.so.0.3000.2 7fa8a1b15000-7fa8a1d14000 ---p 000f3000 08:03 135544 /usr/lib/libglib-2.0.so.0.3000.2 7fa8a1d14000-7fa8a1d15000 r--p 000f2000 08:03 135544 /usr/lib/libglib-2.0.so.0.3000.2 7fa8a1d15000-7fa8a1d16000 rw-p 000f3000 08:03 135544 /usr/lib/libglib-2.0.so.0.3000.2 7fa8a1d16000-7fa8a1d17000 rw-p 00000000 00:00 0 7fa8a1d17000-7fa8a1d38000 r-xp 00000000 08:03 2360114 /lib/ld-2.15.so 7fa8a1f02000-7fa8a1f06000 rw-p 00000000 00:00 0 7fa8a1f34000-7fa8a1f37000 rw-p 00000000 00:00 0 7fa8a1f37000-7fa8a1f38000 r--p 00020000 08:03 2360114 /lib/ld-2.15.so 7fa8a1f38000-7fa8a1f39000 rw-p 00021000 08:03 2360114 /lib/ld-2.15.so 7fa8a1f39000-7fa8a1f3a000 rw-p 00000000 00:00 0 7fff47234000-7fff47255000 rw-p 00000000 00:00 0 [stack] 7fff4738e000-7fff4738f000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] zsh: abort ./gnfree2

Granted, one shouldn't be writing code that runs into this case, but it's kind of funny.

Sunday, January 31, 2010

Grok

A friend of mine wrote a Python script to replace Ack (which is much faster than grep) and the resulting script turned out to be significantly faster than it. I decided then to write my own rough equivalent, called 'grok' (name from another similar program):

grok.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pcre.h>
#include <dirent.h>
#include <limits.h>
#define BLOCK_SIZE 1024
#define OFFSET_COUNT 1
struct {
char * bytes;
off_t length;
} blocks = {NULL, 0};


int search(const char * path, pcre * re) {
FILE * input = fopen(path, "r");
off_t n = 1;
int ovector[OFFSET_COUNT];
int rc;
char first_match = 1;
if(!input) {
perror(path);
return 1;
}
if(!blocks.bytes && !blocks.length) {
// Initialize blocks on first time
blocks.bytes = malloc(BLOCK_SIZE);
memset(blocks.bytes, '\0', BLOCK_SIZE);
blocks.length = BLOCK_SIZE;
}
// Search
while(!feof(input)) {
// Read a line
blocks.bytes[blocks.length - 2] = '\0'; // [blocks.length - 1] will always be '\0' due to fgets() behavior
fgets(blocks.bytes, blocks.length, input);
//printf("%lu/%lu; %d\n", strlen(blocks.bytes), blocks.length, blocks.bytes[blocks.length - 2]);
while(!feof(input) && blocks.bytes[blocks.length - 2] && blocks.bytes[blocks.length - 2] != '\n') {
// Expand blocks as necessary
blocks.length += BLOCK_SIZE;
blocks.bytes = realloc(blocks.bytes, blocks.length);
blocks.bytes[blocks.length - 2] = '\0';
fgets(blocks.bytes + strlen(blocks.bytes), BLOCK_SIZE + 1, input);
//printf("%lu/%lu; %d\n", strlen(blocks.bytes), blocks.length, blocks.bytes[blocks.length - 2]);
}
if(!(feof(input) && !*blocks.bytes)) {
n++;
rc = pcre_exec(re, NULL, blocks.bytes, strlen(blocks.bytes), 0, 0, ovector, OFFSET_COUNT);
if(rc < 0) {
switch(rc) {
case PCRE_ERROR_NOMATCH:
break;
case PCRE_ERROR_BADUTF8:
fprintf(stderr, "Bad UTF-8 at line %lu in %s\nSkipping file (try running with -U option to disable Unicode).\n", n, path);
fclose(input);
return 1;
break;
default:
fprintf(stderr, "Error: %d\n", rc);
break;
}
blocks.bytes[0] = '\0';
continue;
}
if(first_match) {
first_match = 0;
printf("%s:\n", path);
}
printf("%6lu:%s", n, blocks.bytes);
blocks.bytes[0] = '\0';
}
}
// Cleanup
fclose(input);
return 0;
}

int recursive_search(const char * path, pcre * re) {
DIR * dirinfo;
struct dirent * file;
struct stat info;
char fullpath[PATH_MAX], * filepart;
if(!stat(path, &info)) {
if(S_ISREG(info.st_mode)) {
// Regular files
if(search(path, re) == -1)
return -1;
}
else if(S_ISDIR(info.st_mode)) {
// Directories
strcpy(fullpath, path);
strcat(fullpath, "/");
filepart = fullpath + strlen(fullpath);
dirinfo = opendir(path);
while((file = readdir(dirinfo)) != NULL) {
if(*file->d_name != '.') {
strcpy(filepart, file->d_name);
if(recursive_search(fullpath, re) == -1)
return -1;
}
}
closedir(dirinfo);
}
}
else
perror(path);
return 0;
}


int main(int argc, const char * argv[]) {
// Defaults
const char * default_dirs[] = {"."};
const char * progname = *argv;
// General Variables
const char ** dirs;
int erroroffset;
int options = PCRE_UTF8;
off_t i, dirs_length;
const char * error;
pcre * re;
// Initialization
argc--; argv++;
// Parse flags
while(argc && (*argv)[0] == '-' && (*argv)[1] != '-') {
switch((*argv)[1]) {
case 'i':
options |= PCRE_CASELESS;
case 'U':
options &= ~PCRE_UTF8;
case '\0': break;
default:
fprintf(stderr, "Invalid option: %s", *argv);
return -1;
break;
}
argc--; argv++;
}
// Parse arguments
if(argc) {
re = pcre_compile(*argv, options, &error, &erroroffset, NULL);
if(argc > 1) {
dirs = argv + 1;
dirs_length = argc - 1;
}
else {
dirs = default_dirs;
dirs_length = 1;
}
}
else {
fprintf(stderr, "Usage: %s [ -i ] [ -u ] expr [ path1 .. pathN ]\n", progname);
return 1;
}
if(!re) {
fprintf(stderr, "PCRE compilation error at offset %d: %s\n", erroroffset, error);
return 2;
}
// Recursive search
for(i = 0; i < dirs_length; i++) {
if(recursive_search(dirs[i], re) == -1) {
fputs("Ran out of memory.", stderr);
return 128;
}
}
// Cleanup
if(blocks.bytes && blocks.length) {
free(blocks.bytes);
blocks.bytes = NULL;
blocks.length = 0;
}
pcre_free(re);
return 0;
}

As it turns out, my program is able to run twice as fast as his on a given directory tree (small to large).

Friday, October 23, 2009

sizeof/bsizeof

I thought it was about time that I make a few convenient changes to the old sizeof utility. The primary change here is adding support of printing either bytes (as bsizeof, which is useful for piping to sort -k1nr) or size in larger units as appropriate (as sizeof).

sizeof.c

#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>

off_t getsize(const char * path) {
off_t ret = 0;
struct stat info;
struct dirent * file;
DIR * dirinfo;
char d_name[PATH_MAX];
size_t pathlen = strlen(path);
char * app = d_name + pathlen + 1;
if(pathlen < PATH_MAX) {
strcpy(d_name, path);
strcat(d_name, "/");
if(!stat(path, &info)) {
if(S_ISREG(info.st_mode))
ret = info.st_size;
else if(S_ISDIR(info.st_mode)) {
dirinfo = opendir(path);
while((file = readdir(dirinfo)) != NULL) {
if(strcmp(".", file->d_name) && strcmp("..", file->d_name) && pathlen + file->d_reclen + 1 < PATH_MAX) {
strcpy(app, file->d_name);
ret += getsize(d_name);
}
}
closedir(dirinfo);
}
}
}
return ret;
}
int compute_size_string(char * str, off_t size) {
if(size > (1 << 30))
sprintf(str, "%.2lf GiB", ((double) size) / ((double)(1 << 30)));
else if(size > (1 << 20))
sprintf(str, "%.2lf MiB", ((double) size) / ((double)(1 << 20)));
else if(size > (1 << 10))
sprintf(str, "%.2lf kiB", ((double) size) / ((double)(1 << 10)));
else
sprintf(str, "%lu B", size);
return 0;
}
void print_size_computed(off_t size, const char * name) {
char size_string[1024];
compute_size_string(size_string, size);
printf("%-10s %s\n", size_string, name);
}
void print_size(off_t size, const char * name) {
printf("%-40lu %s\n", size, name);
}

int main(int argc, char ** argv) {
int i;
off_t size;
char * bname = basename(argv[0]);
void (* handler)(off_t, const char *) = strcmp(bname, "bsizeof") ? &print_size_computed : &print_size;
for(i = 1; i < argc; i++) {
size = getsize(argv[i]);
if(size)
handler(size, argv[i]);
}
return 0;
}

Friday, April 17, 2009

shparse

Wanting to learn the basics of how to write Python modules in C, I started on this simple function. All it does is use shell-style rules to parse a string into "arguments."

shparse.c

#include <Python.h>
#define INCS 64

enum EMODE {
EMODE_ESC = 1,
EMODE_QUOT = 2,
EMODE_DQUOT = 4
};


static PyObject * shparse_parse(PyObject * self, PyObject * args) {
const Py_UNICODE * cmdstring;
size_t i, x = 0, cplen;
char mode = 0;
PyObject * ret;
Py_UNICODE c, cp[4];

Py_UNICODE * buff = calloc(INCS, sizeof(Py_UNICODE)), * tmp;
size_t bufflen = INCS;



if(!PyArg_ParseTuple(args, "u", &cmdstring))
return NULL;

ret = PyList_New(0);
for(i = 0; cmdstring[i]; i++) {
c = cmdstring[i];
cp[0] = 0; cp[1] = 0; cp[2] = 0; cp[3] = 0; cplen = 0;
if(mode & EMODE_ESC) {
cp[0] = c;
cplen = 1;
mode &= ~EMODE_ESC;
}
else if(mode & EMODE_QUOT) {
switch(c) {
case '\'':
mode &= ~EMODE_QUOT;
break;
default:
cp[0] = c;
cplen = 1;
break;
}
}
else if(mode & EMODE_DQUOT) {
switch(c) {
case '\\':
mode |= EMODE_ESC;
break;
case '"':
mode &= ~EMODE_DQUOT;
break;
default:
cp[0] = c;
cplen = 1;
break;
}
}
else {
switch(c) {
case '\\':
mode |= EMODE_ESC;
break;
case '\'':
mode |= EMODE_QUOT;
break;
case '"':
mode |= EMODE_DQUOT;
break;
case ' ':
if(x > 0) {
x = 0;
PyList_Append(ret, Py_BuildValue("u", buff));
}
break;
default:
cp[0] = c;
cplen = 1;
break;
}
}


if(cp[0]) {
while(x + cplen + 1 >= bufflen) {
bufflen += INCS;
buff = realloc(buff, bufflen);
}
buff[x++] = cp[0];
if(cplen >= 2) {
buff[x++] = cp[1];
if(cplen >= 3) {
buff[x++] = cp[2];
if(cplen == 4)
buff[x++] = cp[2];
else
buff[x] = '\0';
}
else
buff[x] = '\0';
}
else
buff[x] = '\0';
}
}

if(x > 0)
PyList_Append(ret, Py_BuildValue("u", buff));
free(buff);


return ret;
}

static PyMethodDef Methods[] = {
{"parse", shparse_parse, METH_VARARGS, "Parse an input string."},
{NULL, NULL, 0, NULL}
};


PyMODINIT_FUNC initshparse(void) {
Py_InitModule("shparse", Methods);
}

It could be quite useful, and would most definitely be faster than equivalent Python code. By the way, the Python/C API reference is quite good.

Sunday, March 29, 2009

musicdir

I've been organizing my music in a certain way for quite some time: "$ARTIST - $ALBUM/$TRACK - $TITLE.flac". Even so, I've never taken advantage of that structure, at least programmatically. Now I have, with this program:

musicdir.c

#include <glob.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <argp.h>




typedef enum {
MODE_ALL = 1
} MODE;

struct arguments {
char * artist, * album, * title, * root;
size_t artist_len, album_len, title_len, root_len;
int track;
char mode;
} argument = {NULL, NULL, NULL, "/home/music/", 0, 0, 0, 12, -1, 0};

error_t parser(int key, char * arg, struct argp_state * state) {
int track;
switch(key) {
case 'a':
if(arg) {
argument.artist = arg;
argument.artist_len = strlen(arg);
}
break;
case 'c':
if(arg) {
argument.album = arg;
argument.album_len = strlen(arg);
}
break;
case 't':
if(arg) {
argument.title = arg;
argument.title_len = strlen(arg);
}
break;
case 'r':
if(arg) {
argument.root = arg;
argument.root_len = strlen(arg);
}
break;
case 'n':
track = atoi(arg);
if(track > 0)
argument.track = track;
else
return ARGP_ERR_UNKNOWN;
break;
case 'l':
argument.mode |= MODE_ALL;
break;
case ARGP_KEY_ARG:
return ARGP_ERR_UNKNOWN;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
break;
}
return 0;
}


int main(int argc, char ** argv) {
static struct argp_option options[] = {
{ "artist", 'a', "artist", 0, "The artist part of the filename.", 0 },
{ "album", 'c', "album", 0, "The album part of the filename.", 0 },
{ "title", 't', "title", 0, "The title part of the filename.", 0 },
{ "track", 'n', "track", 0, "The track part of the filename, greater than zero.", 0 },
{ "root-dir", 'r', "root-dir", 0, "The root music directory. [/home/music]", 0 },
{ "list-all", 'l', NULL, 0, "List all found files rather than the default (the first file).", 0},
{ 0 }
};
static struct argp args_parsed = {
options,
&parser,
NULL,
"The music directory finder.",
NULL,
NULL
};
int arg_index;
glob_t globs;
int glob_result;
error_t result;
size_t total_len = 16, i;
char * pattern = NULL;
if((result = argp_parse(&args_parsed, argc, argv, 0, &arg_index, &argument)) == 0) {
/* printf("root=%s, artist=%s, album=%s, title=%s, track=%d\n", argument.root, argument.artist, argument.album, argument.title, argument.track); */

/* Allocate string for pattern */
if(argument.root)
total_len += argument.root_len;
if(argument.artist)
total_len += argument.artist_len;
if(argument.album)
total_len += argument.album_len;
if(argument.title)
total_len += argument.title_len;
if(argument.track >= 0)
total_len += 11;
pattern = calloc(total_len, sizeof(char));


/* Copy arguments into pattern */
strcpy(pattern, argument.root);

if(argument.artist)
sprintf(pattern + strlen(pattern), "%s - ", argument.artist);
else
strcat(pattern, "* - ");

if(argument.album)
sprintf(pattern + strlen(pattern), "%s/", argument.album);
else
strcat(pattern, "*/");


if(argument.track >= 0)
sprintf(pattern + strlen(pattern), "%d - ", argument.track);
else
strcat(pattern, "* - ");

if(argument.title)
sprintf(pattern + strlen(pattern), "%s.*", argument.title);
else
strcat(pattern, "*.*");


/* puts(pattern); */
if((glob_result = glob(pattern, GLOB_NOESCAPE, NULL, &globs)) == 0) {
if(globs.gl_pathc > 0) {
puts(*globs.gl_pathv);
if(argument.mode & MODE_ALL)
for(i = 1; i < globs.gl_pathc; i++)
puts(globs.gl_pathv[i]);
}
globfree(&globs);
}
else {
switch(glob_result) {
case GLOB_NOSPACE:
fputs("glob: Unable to allocate memory.\n", stderr);
break;
case GLOB_ABORTED:
fputs("glob: Read error.\n", stderr);
break;
case GLOB_NOMATCH:
fputs("glob: No such file or directory.\n", stderr);
break;
}
}
free(pattern);
}
else
perror("argp_parse");
return 0;
}
% ./musicdir --help
Usage: musicdir [OPTION...]
The music directory finder.

-a, --artist=artist The artist part of the filename.
-c, --album=album The album part of the filename.
-l, --list-all List all found files rather than the default (the
first file).
-n, --track=track The track part of the filename, greater than zero.

-r, --root-dir=root-dir The root music directory. [/home/music]
--root-dir=root-dir The root music directory. [/home/music]
-t, --title=title The title part of the filename.
-?, --help Give this help list
--usage Give a short usage message

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.
% ./musicdir -a aiko
/home/music/aiko - KissHug/01 - KissHug.flac