From 37e4ed696b77455a738fa948b93498c8740721bc Mon Sep 17 00:00:00 2001 From: Johann Latocha Date: Thu, 2 Feb 2012 01:13:50 +0100 Subject: [SERVER] Parsing config file with glib [KERNEL] Bugfixes [CLIENT] Using vid and rid --- src/client/client.c | 47 +++++++++++++++----------- src/config.h | 7 +--- src/kernel/blk.c | 16 +++++---- src/kernel/core.c | 5 +++ src/kernel/dnbd3.h | 3 +- src/kernel/net.c | 26 +++++++++------ src/server/hashtable.c | 78 ------------------------------------------- src/server/hashtable.h | 32 ------------------ src/server/net.c | 19 +++++------ src/server/server.c | 10 +++--- src/server/server.h | 18 ++++++++-- src/server/utils.c | 90 ++++++++++++++++++++++++++++++++++---------------- src/server/utils.h | 7 ++-- src/types.h | 12 ++++--- src/version.h | 4 +-- 15 files changed, 164 insertions(+), 210 deletions(-) delete mode 100644 src/server/hashtable.c delete mode 100644 src/server/hashtable.h (limited to 'src') diff --git a/src/client/client.c b/src/client/client.c index 61724d5..ff95ba1 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -31,15 +31,16 @@ void dnbd3_print_help(char* argv_0) { - printf("Usage: %s -H -p -i -d \n", argv_0); + printf("Usage: %s -h -p -v -r -d \n", argv_0); printf("Start the DNBD3 client.\n"); - printf("-H or --host \t\t Host running dnbd3-server.\n"); + printf("-h or --host \t\t Host running dnbd3-server.\n"); printf("-p or --port \t\t Port used by server.\n"); - printf("-i or --image \t\t Exported image ID.\n"); + printf("-v or --vid \t\t Volume-ID of exported image.\n"); + printf("-r or --rid \t\t Release-ID of exported image.\n"); printf("-d or --device \t\t DNBD3 device name.\n"); printf("-c or --changehost \t Change dnbd3-server on device (DEBUG).\n"); - printf("-h or --help \t\t Show this help text and quit.\n"); - printf("-v or --version \t Show version and quit.\n"); + printf("-H or --help \t\t Show this help text and quit.\n"); + printf("-V or --version \t Show version and quit.\n"); exit(EXIT_SUCCESS); } @@ -54,22 +55,24 @@ int main(int argc, char *argv[]) int fd; char *host = NULL; char *port = NULL; - char *image_id = NULL; + int vid = 0; + int rid = 0; char *dev = NULL; int change_host = 0; int opt = 0; int longIndex = 0; - static const char *optString = "H:p:i:d:c:hv?"; + static const char *optString = "h:p:v:r:d:c:HV?"; static const struct option longOpts[] = { - { "host", required_argument, NULL, 'H' }, + { "host", required_argument, NULL, 'h' }, { "port", required_argument, NULL, 'p' }, - { "image", required_argument, NULL, 'i' }, + { "vid", required_argument, NULL, 'v' }, + { "rid", required_argument, NULL, 'r' }, { "device", required_argument, NULL, 'd' }, { "changehost", required_argument, NULL, 'c' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, }; + { "help", no_argument, NULL, 'H' }, + { "version", no_argument, NULL, 'V' }, }; opt = getopt_long(argc, argv, optString, longOpts, &longIndex); @@ -77,14 +80,17 @@ int main(int argc, char *argv[]) { switch (opt) { - case 'H': + case 'h': host = optarg; break; case 'p': port = optarg; break; - case 'i': - image_id = optarg; + case 'v': + vid = atoi(optarg); + break; + case 'r': + rid = atoi(optarg); break; case 'd': dev = optarg; @@ -93,10 +99,10 @@ int main(int argc, char *argv[]) host = optarg; change_host = 1; break; - case 'h': + case 'H': dnbd3_print_help(argv[0]); break; - case 'v': + case 'V': dnbd3_print_version(); break; case '?': @@ -106,7 +112,7 @@ int main(int argc, char *argv[]) } // change host - if (change_host && host && dev && !port && !image_id) + if (change_host && host && dev && !port && (vid == 0) && (rid == 0)) { fd = open(dev, O_RDONLY); @@ -124,7 +130,7 @@ int main(int argc, char *argv[]) } // connect - if (host && port && dev && image_id) + if (host && port && dev && (vid != 0) && (rid != 0)) { fd = open(dev, O_RDONLY); @@ -134,7 +140,10 @@ int main(int argc, char *argv[]) if (ioctl(fd, IOCTL_SET_PORT, port) < 0) printf("ERROR: ioctl not successful\n"); - if (ioctl(fd, IOCTL_SET_IMAGE, image_id) < 0) + if (ioctl(fd, IOCTL_SET_VID, vid) < 0) + printf("ERROR: ioctl not successful\n"); + + if (ioctl(fd, IOCTL_SET_RID, rid) < 0) printf("ERROR: ioctl not successful\n"); if (ioctl(fd, IOCTL_CONNECT) < 0) diff --git a/src/config.h b/src/config.h index 8cb088e..c839f6e 100644 --- a/src/config.h +++ b/src/config.h @@ -31,13 +31,8 @@ #define DNBD3_BLOCK_SIZE 4096 #define MAX_NUMBER_DEVICES 8 -// configuration file -#define DEFAULT_CONFIG_FILE "/etc/dnbd3-server.conf" -#define MAX_NUMBER_IMAGES 1024 -#define MAX_FILE_NAME 4096 -#define MAX_FILE_ID 8 - // misc +#define DEFAULT_CONFIG_FILE "/etc/dnbd3-server.conf" #define SERVER_PID_FILE "/tmp/dnbd3-server.pid" #endif /* CONFIG_H_ */ diff --git a/src/kernel/blk.c b/src/kernel/blk.c index ccc45dc..c4bbeb1 100644 --- a/src/kernel/blk.c +++ b/src/kernel/blk.c @@ -31,6 +31,9 @@ int dnbd3_blk_add_device(dnbd3_device_t *dev, int minor) INIT_LIST_HEAD(&dev->request_queue_send); INIT_LIST_HEAD(&dev->request_queue_receive); + dev->vid = 0; + dev->rid = 0; + if (!(disk = alloc_disk(1))) { printk("ERROR: dnbd3 alloc_disk failed.\n"); @@ -92,18 +95,17 @@ int dnbd3_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u case IOCTL_SET_HOST: strcpy(lo->host, (char *) arg); break; - case IOCTL_SET_PORT: strcpy(lo->port, (char *) arg); break; - case IOCTL_SET_IMAGE: - strcpy(lo->image_id, (char *) arg); + case IOCTL_SET_VID: + lo->vid = arg; + break; + case IOCTL_SET_RID: + lo->rid = arg; break; case IOCTL_CONNECT: - if (lo->host && lo->port && lo->image_id) - dnbd3_net_connect(lo); - else - return -1; + dnbd3_net_connect(lo); break; case IOCTL_DISCONNECT: dnbd3_net_disconnect(lo); diff --git a/src/kernel/core.c b/src/kernel/core.c index 0e81fdd..086a764 100644 --- a/src/kernel/core.c +++ b/src/kernel/core.c @@ -27,12 +27,14 @@ dnbd3_device_t dnbd3_device[MAX_NUMBER_DEVICES]; static int __init dnbd3_init(void) { int i; + // initialize block device if ((major = register_blkdev(0, "dnbd3")) == 0) { printk("ERROR: dnbd3 register_blkdev failed.\n"); return -EIO; } + // add MAX_NUMBER_DEVICES devices for (i = 0; i < MAX_NUMBER_DEVICES; i++) { @@ -42,6 +44,7 @@ static int __init dnbd3_init(void) return -EIO; } } + printk("INFO: dnbd3 init successful.\n"); return 0; } @@ -49,10 +52,12 @@ static int __init dnbd3_init(void) static void __exit dnbd3_exit(void) { int i; + for (i = 0; i < MAX_NUMBER_DEVICES; i++) { dnbd3_blk_del_device(&dnbd3_device[i]); } + unregister_blkdev(major, "dnbd3"); printk("INFO: dnbd3 exit.\n"); } diff --git a/src/kernel/dnbd3.h b/src/kernel/dnbd3.h index 13af803..8dc0f47 100644 --- a/src/kernel/dnbd3.h +++ b/src/kernel/dnbd3.h @@ -40,7 +40,8 @@ typedef struct // network char host[16]; char port[6]; - char image_id[MAX_FILE_NAME]; + int vid; + int rid; struct socket *sock; struct timer_list hb_timer; struct request hb_request; diff --git a/src/kernel/net.c b/src/kernel/net.c index dba45c6..a83b4b6 100644 --- a/src/kernel/net.c +++ b/src/kernel/net.c @@ -29,14 +29,14 @@ void dnbd3_net_connect(dnbd3_device_t *lo) dnbd3_request_t dnbd3_request; dnbd3_reply_t dnbd3_reply; - if (!lo->host || !lo->port || !lo->image_id) + if (!lo->host || !lo->port || (lo->vid == 0) || (lo->rid == 0)) { - printk("ERROR: Host or port not set."); + printk("ERROR: Host, port, vid or rid not set.\n"); return; } - // TODO: check if allready connected - printk("INFO: Connecting device %s\n", lo->disk->disk_name); + // TODO: check if already connected + printk("INFO: Connecting device %s to %s\n", lo->disk->disk_name, lo->host); // initialize socket if (sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &lo->sock) < 0) @@ -54,15 +54,17 @@ void dnbd3_net_connect(dnbd3_device_t *lo) return; } - // prepare message and send request + // prepare message dnbd3_request.cmd = CMD_GET_SIZE; - strcpy(dnbd3_request.image_id, lo->image_id); + dnbd3_request.vid = lo->vid; + dnbd3_request.rid = lo->rid; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = MSG_WAITALL | MSG_NOSIGNAL; // No SIGPIPE + // send message iov.iov_base = &dnbd3_request; iov.iov_len = sizeof(dnbd3_request); kernel_sendmsg(lo->sock, &msg, &iov, 1, sizeof(dnbd3_request)); @@ -78,9 +80,8 @@ void dnbd3_net_connect(dnbd3_device_t *lo) printk("ERROR: File size returned by server is < 0.\n"); return; } - - printk("INFO: dnbd3 filesize: %llu\n", dnbd3_reply.filesize); set_capacity(lo->disk, dnbd3_reply.filesize >> 9); /* 512 Byte blocks */ + printk("INFO: dnbd3 filesize: %llu\n", dnbd3_reply.filesize); // start sending thread lo->thread_send = kthread_create(dnbd3_net_send, lo, lo->disk->disk_name); @@ -104,8 +105,11 @@ void dnbd3_net_disconnect(dnbd3_device_t *lo) printk("INFO: Disconnecting device %s\n", lo->disk->disk_name); // kill sending and receiving threads - kthread_stop(lo->thread_send); - kthread_stop(lo->thread_receive); + if (lo->thread_send && lo->thread_receive) + { + kthread_stop(lo->thread_send); + kthread_stop(lo->thread_receive); + } // clear sock if (lo->sock) @@ -117,7 +121,7 @@ void dnbd3_net_disconnect(dnbd3_device_t *lo) if (&lo->hb_timer) del_timer(&lo->hb_timer); - // move already send requests to request_queue_send + // move already send requests to request_queue_send again if (!list_empty(&lo->request_queue_receive)) { printk("WARN: Request queue was not empty on %s\n", lo->disk->disk_name); diff --git a/src/server/hashtable.c b/src/server/hashtable.c deleted file mode 100644 index a9fb63c..0000000 --- a/src/server/hashtable.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of the Distributed Network Block Device 3 - * - * Copyright(c) 2011-2012 Johann Latocha - * - * This file may be licensed under the terms of of the - * GNU General Public License Version 2 (the ``GPL''). - * - * Software distributed under the License is distributed - * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See the GPL for the specific language - * governing rights and limitations. - * - * You should have received a copy of the GPL along with this - * program. If not, go to http://www.gnu.org/licenses/gpl.html - * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include - -#include "../config.h" - -char key_buf[MAX_NUMBER_IMAGES * MAX_FILE_ID]; -char value_buf[MAX_NUMBER_IMAGES * MAX_FILE_NAME]; - -char *key_ptr = key_buf; -char *val_ptr = value_buf; - -void dnbd3_ht_create() -{ - (void) hcreate(MAX_NUMBER_IMAGES); -} - -void dnbd3_ht_destroy() -{ - key_ptr = key_buf; - val_ptr = value_buf; - hdestroy(); -} - -int dnbd3_ht_insert(char* key, char* value) -{ - if (strlen(key) > MAX_FILE_ID) - return -1; - if (strlen(value) > MAX_FILE_NAME) - return -2; - - strcpy(key_ptr, key); - strcpy(val_ptr, value); - - ENTRY item; - item.key = key_ptr; - item.data = val_ptr; - - (void) hsearch(item, ENTER); - - key_ptr += strlen(key) + 1; - val_ptr += strlen(value) + 1; - - return 0; -} - -char* dnbd3_ht_search(char* key) -{ - ENTRY *result; - - ENTRY item; - item.key = key; - - if ((result = hsearch(item, FIND)) != NULL) - return result->data; - else - return NULL; -} diff --git a/src/server/hashtable.h b/src/server/hashtable.h deleted file mode 100644 index 3c16dff..0000000 --- a/src/server/hashtable.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of the Distributed Network Block Device 3 - * - * Copyright(c) 2011-2012 Johann Latocha - * - * This file may be licensed under the terms of of the - * GNU General Public License Version 2 (the ``GPL''). - * - * Software distributed under the License is distributed - * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See the GPL for the specific language - * governing rights and limitations. - * - * You should have received a copy of the GPL along with this - * program. If not, go to http://www.gnu.org/licenses/gpl.html - * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef HASHTABLE_H_ -#define HASHTABLE_H_ - -void dnbd3_ht_create(); - -void dnbd3_ht_destroy(); - -int dnbd3_ht_insert(char* key, char* value); - -char* dnbd3_ht_search(char* key); - -#endif /* HASHTABLE_H_ */ diff --git a/src/server/net.c b/src/server/net.c index 2d6b9fc..4f0e5a4 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -32,13 +31,12 @@ #include #include "server.h" -#include "hashtable.h" +#include "utils.h" void *dnbd3_handle_query(void *dnbd3_client) { dnbd3_client_t *client = (dnbd3_client_t *) (uintptr_t) dnbd3_client; int image_file = -1; - off_t filesize = 0; dnbd3_request_t request; dnbd3_reply_t reply; uint16_t cmd; @@ -56,21 +54,19 @@ void *dnbd3_handle_query(void *dnbd3_client) case CMD_GET_SIZE: pthread_spin_lock(&_spinlock); // because of reloading config - image_file = open(dnbd3_ht_search(request.image_id), O_RDONLY); + dnbd3_image_t *image = dnbd3_get_image(request.vid, request.rid); pthread_spin_unlock(&_spinlock); - if (image_file < 0) + if (image) { - printf("ERROR: Client requested an unknown image id.\n"); - filesize = 0; + image_file = open(image->file, O_RDONLY); + reply.filesize = image->filesize; } else { - struct stat st; - fstat(image_file, &st); - filesize = st.st_size; + printf("ERROR: Client requested an unknown image id.\n"); + reply.filesize = 0; } reply.cmd = request.cmd; - reply.filesize = filesize; send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0); break; @@ -94,6 +90,7 @@ void *dnbd3_handle_query(void *dnbd3_client) } close(client->sock); + close(image_file); _dnbd3_clients = g_slist_remove(_dnbd3_clients, client); free(client); printf("INFO: Client %s exit\n", client->ip); diff --git a/src/server/server.c b/src/server/server.c index ad4c20b..a324682 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -31,15 +31,16 @@ #include "server.h" #include "utils.h" -#include "hashtable.h" #include "signal.h" #include "net.h" int _sock; +GSList *_dnbd3_clients = NULL; pthread_spinlock_t _spinlock; char *_config_file_name = DEFAULT_CONFIG_FILE; -GSList *_dnbd3_clients = NULL; +dnbd3_image_t *_images; +size_t _num_images = 0; void dnbd3_print_help(char* argv_0) { @@ -66,7 +67,7 @@ void dnbd3_cleanup() GSList *iterator = NULL; for (iterator = _dnbd3_clients; iterator; iterator = iterator->next) { - dnbd3_client_t *client = iterator->data; + dnbd3_client_t *client = iterator->data; shutdown(client->sock, SHUT_RDWR); pthread_join(*client->thread, NULL); } @@ -74,6 +75,7 @@ void dnbd3_cleanup() g_slist_free(_dnbd3_clients); close(_sock); + free(_images); dnbd3_delete_pid_file(); exit(EXIT_SUCCESS); } @@ -172,7 +174,7 @@ int main(int argc, char* argv[]) dnbd3_client->sock = fd; dnbd3_client->thread = &thread; - _dnbd3_clients = g_slist_append (_dnbd3_clients, dnbd3_client); + _dnbd3_clients = g_slist_append(_dnbd3_clients, dnbd3_client); pthread_create(&(thread), NULL, dnbd3_handle_query, (void *) (uintptr_t) dnbd3_client); } diff --git a/src/server/server.h b/src/server/server.h index 9372c55..303d6ee 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -22,7 +22,8 @@ #define SERVER_H_ #include -#include +#include +#include #include "config.h" #include "../types.h" @@ -34,9 +35,22 @@ typedef struct pthread_t *thread; } dnbd3_client_t; +typedef struct +{ + char *file; + off_t filesize; + char **servers; + size_t num; + int vid; + int rid; +} dnbd3_image_t; + +extern GSList *_dnbd3_clients; extern pthread_spinlock_t _spinlock; extern char *_config_file_name; -extern GSList *_dnbd3_clients; +extern dnbd3_image_t *_images; +extern size_t _num_images; + void dnbd3_cleanup(); diff --git a/src/server/utils.c b/src/server/utils.c index 3a42849..f3a4c5e 100644 --- a/src/server/utils.c +++ b/src/server/utils.c @@ -18,12 +18,15 @@ * */ +#include "server.h" #include "utils.h" -#include "hashtable.h" #include #include #include +#include +#include + void dnbd3_write_pid_file(pid_t pid) { @@ -65,52 +68,81 @@ void dnbd3_delete_pid_file() } } -void dnbd3_load_config(char* config_file_name) +void dnbd3_send_signal(int signum) +{ + pid_t pid = dnbd3_read_pid_file(); + if (pid != 0) + { + if (kill(pid, signum) != 0) + { + printf("ERROR: dnbd3-server is not running\n"); + dnbd3_delete_pid_file(); + } + } + else + { + printf("ERROR: dnbd3-server is not running\n"); + } +} + +void dnbd3_load_config(char *file) { - dnbd3_ht_create(); - FILE *config_file = fopen(config_file_name, "r"); + int fd; + gint i; + GKeyFile* gkf; - if (config_file == NULL) + gkf = g_key_file_new(); + if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL)) { - printf("ERROR: Config file not found: %s\n", config_file_name); + printf("ERROR: Config file not found: %s\n", file); exit(EXIT_FAILURE); } - char line[MAX_FILE_NAME + 1 + MAX_FILE_ID]; - char* image_name = NULL; - char* image_id = NULL; + gchar **groups = NULL; + groups = g_key_file_get_groups(gkf, &_num_images); + _images = calloc(_num_images, sizeof(dnbd3_image_t)); - while (fgets(line, sizeof(line), config_file) != NULL) + for (i = 0; i < _num_images; i++) { - sscanf(line, "%as %as", &image_name, &image_id); - if (dnbd3_ht_insert(image_id, image_name) < 0) + _images[i].file = g_key_file_get_string(gkf, groups[i], "file", NULL); + _images[i].servers = g_key_file_get_string_list(gkf, groups[i], "servers", &_images[i].num, NULL); + _images[i].vid = g_key_file_get_integer(gkf, groups[i], "vid", NULL); + _images[i].rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL); + + fd = open(_images[i].file, O_RDONLY); + if (fd > 0) + { + struct stat st; + fstat(fd, &st); + _images[i].filesize = st.st_size; + } + else { - printf("ERROR: Image name or ID is too big\n"); - exit(EXIT_FAILURE); + printf("ERROR: Image not found: %s\n", _images[i].file); } + close(fd); } - fclose(config_file); + + g_strfreev(groups); + g_key_file_free (gkf); } void dnbd3_reload_config(char* config_file_name) { - dnbd3_ht_destroy(); + free(_images); + _num_images = 0; dnbd3_load_config(config_file_name); } -void dnbd3_send_signal(int signum) +dnbd3_image_t* dnbd3_get_image(int vid, int rid) { - pid_t pid = dnbd3_read_pid_file(); - if (pid != 0) - { - if (kill(pid, signum) != 0) - { - printf("ERROR: dnbd3-server is not running\n"); - dnbd3_delete_pid_file(); - } - } - else - { - printf("ERROR: dnbd3-server is not running\n"); + // TODO: find better data structure + dnbd3_image_t *result = NULL; + int i; + for (i = 0; i < _num_images; ++i) { + if (_images[i].vid == vid && _images[i].rid == rid) + result = &_images[i]; + } + return result; } diff --git a/src/server/utils.h b/src/server/utils.h index ab8f839..6ac51ff 100644 --- a/src/server/utils.h +++ b/src/server/utils.h @@ -30,9 +30,10 @@ pid_t dnbd3_read_pid_file(); void dnbd3_write_pid_file(pid_t pid); void dnbd3_delete_pid_file(); -void dnbd3_load_config(char* config_file_name); -void dnbd3_reload_config(char* config_file_name); - void dnbd3_send_signal(int signum); +void dnbd3_load_config(char *file); +void dnbd3_reload_config(char* config_file_name); +dnbd3_image_t* dnbd3_get_image(int vid, int rid); + #endif /* UTILS_H_ */ diff --git a/src/types.h b/src/types.h index 9bc0795..7f5c392 100644 --- a/src/types.h +++ b/src/types.h @@ -27,22 +27,24 @@ #define DNBD3_MAGIC 'd' #define IOCTL_SET_HOST _IO(0xab, 1) #define IOCTL_SET_PORT _IO(0xab, 2) -#define IOCTL_SET_IMAGE _IO(0xab, 3) -#define IOCTL_CONNECT _IO(0xab, 4) -#define IOCTL_DISCONNECT _IO(0xab, 5) +#define IOCTL_SET_VID _IO(0xab, 3) +#define IOCTL_SET_RID _IO(0xab, 4) +#define IOCTL_CONNECT _IO(0xab, 5) +#define IOCTL_DISCONNECT _IO(0xab, 6) // network #define CMD_GET_BLOCK 1 #define CMD_GET_SIZE 2 -#define CMD_PING 3 +#define CMD_PING 3 #pragma pack(1) typedef struct { uint16_t cmd; + uint16_t vid; + uint16_t rid; uint64_t offset; uint64_t size; - char image_id[MAX_FILE_ID]; char handle[8]; } dnbd3_request_t; #pragma pack(0) diff --git a/src/version.h b/src/version.h index bbaaf41..2664741 100644 --- a/src/version.h +++ b/src/version.h @@ -21,7 +21,7 @@ #ifndef VERSION_H_ #define VERSION_H_ -#define VERSION_STRING "0.1.0" -#define VERSION_NUMBER 010 +#define VERSION_STRING "0.3.0" +#define VERSION_NUMBER 030 #endif /* VERSION_H_ */ -- cgit v1.2.3-55-g7522