Browse code

- new command line tool which communicates with the ctl module (using the new binary protocol) - supports various connection methods: udp, tcp, unix sockets (datagram and stream) - can do basic reply formatting (see -f ) - supports both command line and interactive modes - if compiled with libreadline supports command completion in the interactive mode - depends only on libreadline/libedit if compiled with command completion support Example: sercmd -s unixd:/tmp/unix_dgram -f 'pid:%v desc:"%v"\n' core.ps sercmd # enters interactive mode, uses default unix:/tmp/ser_ctl socket sercmd ps sercmd who (see also sercmd -h and utils/sercmd/EXAMPLES)

Andrei Pelinescu-Onciul authored on 23/02/2006 20:07:45
Showing 8 changed files
... ...
@@ -10,6 +10,14 @@ new archs:
10 10
 
11 11
 
12 12
 modules:
13
+ - ctl        - new  fifo/unixsocket/xmlrpc like module, using a space 
14
+                efficient binary encoding for the requests.
15
+                It supports multiple clients on tcp, udp, unix stream or
16
+                unix datagram modes. By default (no modparams) it opens
17
+                one unix stream control socket in /tmp/ser_ctl.
18
+                It also includes extended fifo support: multiple fifos,
19
+                fifo over tcp, udp and unix sockets (see ctl/ctl.cfg).
20
+                Use utils/sercmd/sercmd to send commands to it.
13 21
  - dispatcher - added hashing after request uri and to uri
14 22
               - added a new flag parameter which can be used (for now) to
15 23
                 select only the username or the username, host and port when
... ...
@@ -41,6 +49,24 @@ modules:
41 49
               Vias a.s.o) and not on the original message
42 50
  
43 51
 core:
52
+ - added named routes: names can be used instead of numbers in all the
53
+   route commads or route declarations. route(number) is equivalent to
54
+   route("number").
55
+   Example:
56
+     route("test");
57
+      route["test"]{
58
+           ...
59
+     }
60
+ - added named flags, declared at the beginning of the config file with:
61
+     flags  flag1_name[:position],  flag2_name ...
62
+   Example:
63
+       flags test, a:1, b:2 ;
64
+       route{
65
+              setflag(test);
66
+              if (isflagset(a)){ # equiv. to isflagset(1)
67
+                ....
68
+              }
69
+              resetflag(b);  # equiv. to resetflag(2) 
44 70
  - added return [val] which returns from a route. if no value is specified, or
45 71
    a route reaches its end without executing a return statement, it returns 1.
46 72
    If return is used in the top level route is equivalent with exit [val].
... ...
@@ -123,6 +149,14 @@ new config variables:
123 149
    tcp_max_connections = no. - maximum number of tcp connections (if the number
124 150
       is exceeded no new tcp connections will be accepted). Default: 2048.
125 151
 
152
+tools:
153
+  utils/sercmd - command line serctl like tool for interrogating ser ctl
154
+                 module (uses the binrpc encoding). Supports various
155
+                 connection methods (udp, tcp, unix stream & datagram sockets),
156
+                 reply formating (see -f, e.g. sercmd -f "pid:%v %v\n" core.ps)
157
+                 , interactive mode, command line completion (if compiled with 
158
+                 libreadline) a.s.o.
159
+
126 160
 
127 161
 0.9.4 fixes/improvements (0.9.4 is a bug fix release for 0.9.3)
128 162
  
129 163
new file mode 100644
... ...
@@ -0,0 +1,59 @@
1
+# $id$
2
+
3
+sercmd usage examples
4
+
5
+
6
+help:
7
+	sercmd  unixd:/tmp/unix_dgram -h
8
+
9
+use an udp ser control socket:
10
+ ser config:
11
+  loadmodule "modules/ctl/ctl.so"
12
+  modparam("ctl", "binrpc", "udp:localhost:2046")
13
+  modparam("ctl", "binrpc", "tcp:localhost:2046")
14
+  modparam("ctl", "binrpc", "unixs:/tmp/unix_stream")
15
+  modparam("ctl", "binrpc", "unixd:/tmp/unix_dgram")
16
+
17
+ sercmd:
18
+	sercmd -s udp:localhost:2046 core.version
19
+
20
+use a tcp socket:
21
+	sercmd -s tcp:localhost:2046 core.version
22
+
23
+use a stream unix socket:
24
+	sercmd -s unixs:/tmp/unix_stream core.version
25
+
26
+use a datagram unix socket:
27
+	sercmd -s unixd:/tmp/unix_dgram core.version
28
+
29
+
30
+
31
+list available commands on ser side:
32
+	sercmd -s unixd:/tmp/unix_drgam ls
33
+
34
+list all available commands (including sercmd builtins or aliases):
35
+	sercmd -s unixd:/tmp/unix_dgram ?
36
+or
37
+	sercmd -s unixd:/tmp/unix_dgram help
38
+
39
+get help on one command:
40
+	sercmd -s unixd:/tmp/unix_dgram help core.ps
41
+
42
+list ser processes:
43
+	sercmd -s unixd:/tmp/unix_dgram ps
44
+
45
+send an rpc command to ser:
46
+	sercmd -s unixd:/tmp/unix_dgram core.shmmem
47
+
48
+format the output:
49
+	sercmd -s unixd:/tmp/unix_dgram -f 'pid:%v desc:"%v"\n' core.ps
50
+	(note: you could use just ps instead of core.ps)
51
+	
52
+format the output as csv:
53
+	sercmd -s unixd:/tmp/unix_dgram -f '%v,' core.ps
54
+
55
+enter interactive mode:
56
+	sercmd -s unixd:/tmp/unix_dgram
57
+	(note: type help,or ? to see the command list, tab expansion should also
58
+	 work)
59
+
0 60
new file mode 100644
... ...
@@ -0,0 +1,22 @@
1
+# $Id$
2
+
3
+include ../../Makefile.defs
4
+
5
+auto_gen=
6
+NAME=sercmd
7
+RELEASE=0.1
8
+use_readline=1
9
+DEFS:= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' \
10
+		$(filter -D%HAVE -DARCH% -DOS% -D__CPU% -D__OS%, $(DEFS))
11
+LIBS:=$(filter-out -lfl  -ldl -lpthread -lssl -lcrypto, $(LIBS))
12
+
13
+ifeq ($(use_readline),1)
14
+	DEFS+=-DUSE_READLINE
15
+	LIBS+=-lreadline
16
+endif
17
+
18
+include ../../Makefile.sources
19
+include ../../Makefile.rules
20
+
21
+
22
+modules:
0 23
new file mode 100644
... ...
@@ -0,0 +1,4 @@
1
+
2
+- help for internal commands & aliases
3
+- send timeout
4
+- reconnect?
0 5
new file mode 100644
... ...
@@ -0,0 +1,34 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * sercmd GPL license, standard disclaimer and copyright
5
+ */
6
+
7
+#ifndef __license_h_
8
+#define __license_h_
9
+
10
+#define COPYRIGHT  "Copyright 2006 iptelorg GmbH"
11
+#define DISCLAIMER \
12
+"This is free software with ABSOLUTELY NO WARRANTY.\n\
13
+For details type `warranty'."
14
+
15
+
16
+#define LICENSE \
17
+"    This program is free software; you can redistribute it and/or modify\n\
18
+    it under the terms of the GNU General Public License as published by\n\
19
+    the Free Software Foundation; either version 2 of the License , or\n\
20
+    (at your option) any later version.\n\
21
+\n\
22
+    This program is distributed in the hope that it will be useful,\n\
23
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
24
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
25
+    GNU General Public License for more details.\n\
26
+\n\
27
+    You should have received a copy of the GNU General Public License\n\
28
+    along with this program. If not, write to\n\
29
+\n\
30
+       The Free Software Foundation, Inc.\n\
31
+       51 Franklin Street, Fifth Floor,\n\
32
+       Boston, MA 02110-1301, USA."
33
+
34
+#endif
0 35
new file mode 100644
... ...
@@ -0,0 +1,267 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2006 iptelorg GmbH
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+/* History:
28
+ * --------
29
+ *  2006-02-20  created by andrei
30
+ */
31
+
32
+
33
+#include <stdio.h>
34
+#include <stdlib.h> /* malloc */
35
+#include <string.h>
36
+#include <netdb.h> /* getservbyname*/
37
+
38
+#include "parse_listen_id.h"
39
+
40
+/* ser source compat. defines*/
41
+#define pkg_malloc malloc
42
+#define pkg_free   free
43
+
44
+#ifdef __SUNPRO_C
45
+#define DBG(...)
46
+#define LOG(lev, ...) fprintf(stderr,  __VA_ARGS__)
47
+#else
48
+#define DBG(fmt, args...)
49
+#define LOG(lev, fmt, args...) fprintf(stderr, fmt, ## args)
50
+#endif
51
+
52
+
53
+
54
+
55
+
56
+/* converts a str to an u. short, returns the u. short and sets *err on
57
+ * error and if err!=null
58
+  */
59
+static inline unsigned short str2s(const char* s, unsigned int len,
60
+									int *err)
61
+{
62
+	unsigned short ret;
63
+	int i;
64
+	unsigned char *limit;
65
+	unsigned char *init;
66
+	unsigned char* str;
67
+
68
+	/*init*/
69
+	str=(unsigned char*)s;
70
+	ret=i=0;
71
+	limit=str+len;
72
+	init=str;
73
+
74
+	for(;str<limit ;str++){
75
+		if ( (*str <= '9' ) && (*str >= '0') ){
76
+				ret=ret*10+*str-'0';
77
+				i++;
78
+				if (i>5) goto error_digits;
79
+		}else{
80
+				/* error unknown char */
81
+				goto error_char;
82
+		}
83
+	}
84
+	if (err) *err=0;
85
+	return ret;
86
+
87
+error_digits:
88
+	DBG("str2s: ERROR: too many letters in [%.*s]\n", (int)len, init);
89
+	if (err) *err=1;
90
+	return 0;
91
+error_char:
92
+	DBG("str2s: ERROR: unexpected char %c in %.*s\n", *str, (int)len, init);
93
+	if (err) *err=1;
94
+	return 0;
95
+}
96
+
97
+
98
+
99
+/* parse proto:address:port   or proto:address */
100
+/* returns struct id_list on success (pkg_malloc'ed), 0 on error
101
+ * WARNING: it will add \0 in the string*/
102
+/* parses:
103
+ *     tcp|udp|unix:host_name:port
104
+ *     tcp|udp|unix:host_name
105
+ *     host_name:port
106
+ *     host_name
107
+ * 
108
+ *
109
+ *     where host_name=string, ipv4 address, [ipv6 address],
110
+ *         unix socket path (starts with '/')
111
+ */
112
+struct id_list* parse_listen_id(char* l, int len, enum socket_protos def)
113
+{
114
+	char* p;
115
+	enum socket_protos proto;
116
+	char* name;
117
+	char* port_str;
118
+	int port;
119
+	int err;
120
+	struct servent* se;
121
+	char* s;
122
+	struct id_list* id;
123
+	
124
+	s=pkg_malloc((len+1)*sizeof(char));
125
+	if (s==0){
126
+		LOG(L_ERR, "ERROR:parse_listen_id: out of memory\n");
127
+		goto error;
128
+	}
129
+	memcpy(s, l, len);
130
+	s[len]=0; /* null terminate */
131
+	
132
+	/* duplicate */
133
+	proto=UNKNOWN_SOCK;
134
+	port=0;
135
+	name=0;
136
+	port_str=0;
137
+	p=s;
138
+	
139
+	if ((*p)=='[') goto ipv6;
140
+	/* find proto or name */
141
+	for (; *p; p++){
142
+		if (*p==':'){
143
+			*p=0;
144
+			if (strcasecmp("tcp", s)==0){
145
+				proto=TCP_SOCK;
146
+				goto find_host;
147
+			}else if (strcasecmp("udp", s)==0){
148
+				proto=UDP_SOCK;
149
+				goto find_host;
150
+			}else if ((strcasecmp("unix", s)==0)||(strcasecmp("unixd", s)==0)){
151
+				proto=UNIXS_SOCK;
152
+				goto find_host;
153
+			}else if (strcasecmp("unixs", s)==0){
154
+				proto=UNIXS_SOCK;
155
+				goto find_host;
156
+#ifdef USE_FIFO
157
+			}else if (strcasecmp("fifo", s)==0){
158
+				proto=FIFO_SOCK;
159
+				goto find_host;
160
+#endif
161
+			}else{
162
+				proto=UNKNOWN_SOCK;
163
+				/* this might be the host */
164
+				name=s;
165
+				goto find_port;
166
+			}
167
+		}
168
+	}
169
+	name=s;
170
+	goto end; /* only name found */
171
+find_host:
172
+	p++;
173
+	if (*p=='[') goto ipv6;
174
+	name=p;
175
+	for (; *p; p++){
176
+		if ((*p)==':'){
177
+			*p=0;
178
+			goto find_port;
179
+		}
180
+	}
181
+	goto end; /* nothing after name */
182
+ipv6:
183
+	name=p;
184
+	p++;
185
+	for(;*p;p++){
186
+		if(*p==']'){
187
+			if(*(p+1)==':'){
188
+				p++; *p=0;
189
+				goto find_port;
190
+			}else if (*(p+1)==0) goto end;
191
+		}else{
192
+			goto error;
193
+		}
194
+	}
195
+	
196
+find_port:
197
+	p++;
198
+	port_str=(*p)?p:0;
199
+	
200
+end:
201
+	/* fix all the stuff */
202
+	if (name==0) goto error;
203
+	if (proto==UNKNOWN_SOCK){
204
+		/* try to guess */
205
+		if (port_str){
206
+			switch(def){
207
+				case TCP_SOCK:
208
+				case UDP_SOCK:
209
+					proto=def;
210
+					break;
211
+				default:
212
+					proto=TCP_SOCK;
213
+					DBG("guess:%s is a tcp socket\n", name);
214
+			}
215
+		}else if (name && strchr(name, '/')){
216
+			switch(def){
217
+				case TCP_SOCK:
218
+				case UDP_SOCK:
219
+					DBG("guess:%s is a unix socket\n", name);
220
+					proto=UNIXD_SOCK;
221
+					break;
222
+				default:
223
+					/* def is filename based => use default */
224
+					proto=def;
225
+			}
226
+		}else{
227
+			/* using default */
228
+			proto=def;
229
+		}
230
+	}
231
+	if (port_str){
232
+		port=str2s(port_str, strlen(port_str), &err);
233
+		if (err){
234
+			/* try getservbyname */
235
+			se=getservbyname(port_str, 
236
+					(proto==TCP_SOCK)?"tcp":(proto==UDP_SOCK)?"udp":0);
237
+			if (se) port=ntohs(se->s_port);
238
+			else goto error;
239
+		}
240
+	}else{
241
+		/* no port, check if the hostname is a port 
242
+		 * (e.g. tcp:3012 == tcp:*:3012 */
243
+		if (proto==TCP_SOCK|| proto==UDP_SOCK){
244
+			port=str2s(name, strlen(name), &err);
245
+			if (err){
246
+				port=0;
247
+			}else{
248
+				name="*"; /* inaddr any  */
249
+			}
250
+		}
251
+	}
252
+	id=pkg_malloc(sizeof(struct id_list));
253
+	if (id==0){
254
+		LOG(L_ERR, "ERROR:parse_listen_id: out of memory\n");
255
+		goto error;
256
+	}
257
+	id->name=name;
258
+	id->proto=proto;
259
+	id->data_proto=P_BINRPC;
260
+	id->port=port;
261
+	id->buf=s;
262
+	id->next=0;
263
+	return id;
264
+error:
265
+	if (s) pkg_free(s);
266
+	return 0;
267
+}
0 268
new file mode 100644
... ...
@@ -0,0 +1,59 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2006 iptelorg GmbH
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+/* History:
28
+ * --------
29
+ *  2006-02-20  created by andrei
30
+ */
31
+
32
+
33
+#ifndef parse_listen_id_h
34
+#define parse_listen_id_h
35
+
36
+enum payload_proto	{ P_BINRPC , P_FIFO };
37
+
38
+enum socket_protos	{	UNKNOWN_SOCK=0, UDP_SOCK, TCP_SOCK, 
39
+						UNIXS_SOCK, UNIXD_SOCK
40
+#ifdef USE_FIFO
41
+							, FIFO_SOCK
42
+#endif
43
+};
44
+
45
+
46
+
47
+struct id_list{
48
+	char* name;
49
+	enum socket_protos proto;
50
+	enum payload_proto data_proto;
51
+	int port;
52
+	char* buf; /* name points somewhere here */
53
+	struct id_list* next;
54
+};
55
+
56
+
57
+struct id_list* parse_listen_id(char* l, int len, enum socket_protos def);
58
+
59
+#endif
0 60
new file mode 100644
... ...
@@ -0,0 +1,1467 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2006 iptelorg GmbH
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+/*
28
+ * send commands using binrpc
29
+ *
30
+ * History:
31
+ * --------
32
+ *  2006-02-14  created by andrei
33
+ */
34
+
35
+
36
+#include <stdlib.h> /* exit, abort */
37
+#include <stdio.h>
38
+#include <string.h>
39
+#include <unistd.h>
40
+#include <errno.h>
41
+#include <ctype.h> /* isprint */
42
+#include <sys/socket.h>
43
+#include <sys/un.h> /* unix sock*/
44
+#include <netinet/in.h> /* udp sock */
45
+#include <sys/uio.h> /* writev */
46
+#include <netdb.h> /* gethostbyname */
47
+#include <time.h> /* time */
48
+
49
+#ifdef USE_READLINE
50
+#include <readline/readline.h>
51
+#include <readline/history.h>
52
+#endif
53
+
54
+#include "parse_listen_id.h"
55
+#include "license.h"
56
+
57
+#include "../../modules/ctl/ctl_defaults.h" /* default socket & port */
58
+#include "../../modules/ctl/binrpc.h"
59
+#include "../../modules/ctl/binrpc.c" /* ugly hack */
60
+
61
+
62
+#ifndef NAME
63
+#define NAME    "sercmd"
64
+#endif
65
+#ifndef VERSION
66
+#define VERSION "0.1"
67
+#endif
68
+
69
+#define IOVEC_CNT 20
70
+#define BUF_SIZE  65535
71
+#define MAX_LINE_SIZE 1024 /* for non readline mode */
72
+#define MAX_REPLY_SIZE  4096
73
+#define MAX_BODY_SIZE   4096
74
+#define MAX_BINRPC_ARGS  128
75
+
76
+
77
+#ifndef UNIX_PATH_MAX
78
+#define UNIX_PATH_MAX 108
79
+#endif
80
+
81
+static char id[]="$Id$";
82
+static char version[]= NAME " " VERSION;
83
+static char compiled[]= __TIME__ " " __DATE__;
84
+static char help_msg[]="\
85
+Usage: " NAME " [options][-s address] [ cmd ]\n\
86
+Options:\n\
87
+    -s address  unix socket name or host name to send the commands on\n\
88
+    -R name     force reply socket name, for the unix datagram socket mode\n\
89
+    -D dir      create the reply socket in the directory <dir> if no reply \n\
90
+                socket is forced (-R) and a unix datagram socket is selected\n\
91
+                as the transport\n\
92
+    -f format   print the result using format. Format is a string containing\n\
93
+                %v at the places where values read from the reply should be\n\
94
+                substituted. To print '%v', escape it using '%': %%v.\n\
95
+    -v          Verbose       \n\
96
+    -V          Version number\n\
97
+    -h          This help message\n\
98
+address:\n\
99
+    [proto:]name[:port]   where proto is one of tcp, udp, unixs or unixd\n\
100
+                          e.g.:  tcp:localhost:2048 , unixs:/tmp/ser_ctl\n\
101
+cmd:\n\
102
+    method  [arg1 [arg2...]]\n\
103
+arg:\n\
104
+     string or number; to force a number to be interpreted as string \n\
105
+     prefix it by \"s:\", e.g. s:1\n\
106
+Example:\n\
107
+        " NAME " -s unixs:/tmp/ser_unix system.listMethods\n\
108
+        " NAME " -f \"pid: %v  desc: %v\\n\" -s udp:localhost:2047 core.ps \n\
109
+        " NAME " ps  # uses default ctl socket \n\
110
+        " NAME "     # enters interactive mode on the default socket \n\
111
+        " NAME " -s tcp:localhost # interactive mode, default port \n\
112
+";
113
+
114
+
115
+int verbose=0;
116
+char* reply_socket=0; /* unix datagram reply socket name */
117
+char* sock_dir=0;     /* same as above, but only the directory */
118
+char* unix_socket=0;
119
+struct sockaddr_un mysun;
120
+int quit; /* used only in interactive mode */
121
+
122
+struct binrpc_val* rpc_array;
123
+int rpc_no=0;
124
+
125
+
126
+
127
+
128
+#define IOV_SET(vect, str) \
129
+	do{\
130
+		(vect).iov_base=(str); \
131
+		(vect).iov_len=strlen((str)); \
132
+	}while(0)
133
+
134
+
135
+#define INT2STR_MAX_LEN  (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */
136
+
137
+/* returns a pointer to a static buffer containing l in asciiz & sets len */
138
+static inline char* int2str(unsigned int l, int* len)
139
+{
140
+	static char r[INT2STR_MAX_LEN];
141
+	int i;
142
+	
143
+	i=INT2STR_MAX_LEN-2;
144
+	r[INT2STR_MAX_LEN-1]=0; /* null terminate */
145
+	do{
146
+		r[i]=l%10+'0';
147
+		i--;
148
+		l/=10;
149
+	}while(l && (i>=0));
150
+	if (l && (i<0)){
151
+		fprintf(stderr, "BUG: int2str: overflow\n");
152
+	}
153
+	if (len) *len=(INT2STR_MAX_LEN-2)-i;
154
+	return &r[i+1];
155
+}
156
+
157
+
158
+
159
+static char* trim_ws(char* l)
160
+{
161
+	char* ret;
162
+	
163
+	for(;*l && ((*l==' ')||(*l=='\t')||(*l=='\n')||(*l=='\r')); l++);
164
+	ret=l;
165
+	if (*ret==0) return ret;
166
+	for(l=l+strlen(l)-1; (l>ret) && 
167
+			((*l==' ')||(*l=='\t')||(*l=='\n')||(*l=='\r')); l--);
168
+	*(l+1)=0;
169
+	return ret;
170
+}
171
+
172
+
173
+
174
+int gen_cookie()
175
+{
176
+	return rand();
177
+}
178
+
179
+
180
+
181
+struct binrpc_cmd{
182
+	char* method;
183
+	int argc;
184
+	struct binrpc_val argv[MAX_BINRPC_ARGS];
185
+};
186
+
187
+
188
+struct cmd_alias{
189
+	char* name;
190
+	char* method;
191
+	char* format; /* reply print format */
192
+};
193
+
194
+
195
+struct sercmd_builtin{
196
+	char* name;
197
+	int (*f)(int, struct binrpc_cmd*);
198
+	char* doc;
199
+};
200
+
201
+
202
+static int sercmd_help(int s, struct binrpc_cmd* cmd);
203
+static int sercmd_ver(int s, struct binrpc_cmd* cmd);
204
+static int sercmd_quit(int s, struct binrpc_cmd* cmd);
205
+static int sercmd_warranty(int s, struct binrpc_cmd* cmd);
206
+
207
+
208
+static struct cmd_alias cmd_aliases[]={
209
+	{	"ps",			"core.ps",				"%v\t%v\n"	},
210
+	{	"list",			"system.listMethods",	0			},
211
+	{	"ls",			"system.listMethods",	0			},
212
+	{	"server",		"core.version",			0			},
213
+	{	"serversion",	"core.version",			0			},
214
+	{	"who",			"ctl.who",				"[%v] %v: %v %v -> %v %v\n"},
215
+	{	"listen",		"ctl.listen",			"[%v] %v: %v %v\n"},
216
+	{0,0,0}
217
+};
218
+
219
+
220
+static struct sercmd_builtin builtins[]={
221
+	{	"?",		sercmd_help, "help"},
222
+	{	"help",		sercmd_help, "displays help for a command"},
223
+	{	"version",	sercmd_ver,  "displays " NAME "version"},
224
+	{	"quit",		sercmd_quit, "exits " NAME },
225
+	{	"exit",		sercmd_quit, "exits " NAME },
226
+	{	"warranty",		sercmd_warranty, "displays " NAME "'s warranty info"},
227
+	{	"license",		sercmd_warranty, "displays " NAME "'s license"},
228
+	{0,0}
229
+};
230
+
231
+
232
+
233
+#ifdef USE_READLINE
234
+/* commands for which we complete the params to other command names */
235
+char* complete_params[]={
236
+	"?",
237
+	"h",
238
+	"help",
239
+	"system.methodSignature",
240
+	"system.methodHelp",
241
+	0
242
+};
243
+#endif
244
+
245
+
246
+
247
+static int parse_arg(struct binrpc_val* v, char* arg)
248
+{
249
+	int i;
250
+	double f;
251
+	char* tmp;
252
+	int len;
253
+	
254
+	i=strtol(arg, &tmp, 10);
255
+	if ((tmp==0) || (*tmp)){
256
+		f=strtod(arg, &tmp);
257
+		if ((tmp==0) || (*tmp)){
258
+			/* not an int or a float => string */
259
+			len=strlen(arg);
260
+			if ((len>=2) && (arg[0]=='s') && (arg[1]==':')){
261
+				tmp=&arg[2];
262
+				len-=2;
263
+			}else{
264
+				tmp=arg;
265
+			}
266
+			v->type=BINRPC_T_STR;
267
+			v->u.strval.s=tmp;
268
+			v->u.strval.len=len;
269
+		}else{ /* float */
270
+			v->type=BINRPC_T_DOUBLE;
271
+			v->u.fval=f;
272
+		}
273
+	}else{ /* int */
274
+		v->type=BINRPC_T_INT;
275
+		v->u.intval=i;
276
+	}
277
+	return 0;
278
+}
279
+
280
+
281
+
282
+static int parse_cmd(struct binrpc_cmd* cmd, char** argv, int count)
283
+{
284
+	int r;
285
+	
286
+	cmd->method=argv[0];
287
+	if ((count-1)>MAX_BINRPC_ARGS){
288
+		fprintf(stderr,  "ERROR: too many args %d, only %d allowed\n",
289
+					count-1, MAX_BINRPC_ARGS);
290
+		return -1;
291
+	}
292
+	for (r=1; r<count; r++){
293
+		if (parse_arg(&cmd->argv[r-1], argv[r])<0)
294
+			return -1;
295
+	}
296
+	cmd->argc=r-1;
297
+	return 0;
298
+}
299
+
300
+
301
+void print_binrpc_val(struct binrpc_val* v, int ident)
302
+{
303
+	int r;
304
+
305
+	if ((v->type==BINRPC_T_STRUCT) && !v->u.end)
306
+		ident--; /* fix to have strut beg. idented differently */
307
+	for (r=0; r<ident; r++) putchar('	');
308
+	if (v->name.s){
309
+		printf("%.*s: ", v->name.len, v->name.s);
310
+	}
311
+	switch(v->type){
312
+		case BINRPC_T_INT:
313
+			printf("%d", v->u.intval);
314
+			break;
315
+		case BINRPC_T_STR:
316
+		case BINRPC_T_BYTES:
317
+			printf("%.*s", v->u.strval.len, v->u.strval.s);
318
+			break;
319
+		case BINRPC_T_ARRAY:
320
+			printf("%c", (v->u.end)?']':'[');
321
+			break;
322
+		case BINRPC_T_STRUCT:
323
+			printf("%c", (v->u.end)?'}':'{');
324
+			break;
325
+		default:
326
+			printf("ERROR: unknown type %d\n", v->type);
327
+	};
328
+}
329
+
330
+
331
+
332
+/* opens,  and  connects on a STREAM unix socket
333
+ * returns socket fd or -1 on error */
334
+int connect_unix_sock(char* name, int type)
335
+{
336
+	struct sockaddr_un ifsun;
337
+	int s;
338
+	int len;
339
+	int ret;
340
+	int retries;
341
+	
342
+	retries=0;
343
+	s=-1;
344
+	memset(&ifsun, 0, sizeof (struct sockaddr_un));
345
+	len=strlen(name);
346
+	if (len>UNIX_PATH_MAX){
347
+		fprintf(stderr, "ERROR: connect_unix_sock: name too long "
348
+				"(%d > %d): %s\n", len, UNIX_PATH_MAX, name);
349
+		goto error;
350
+	}
351
+	ifsun.sun_family=AF_UNIX;
352
+	memcpy(ifsun.sun_path, name, len);
353
+#ifdef HAVE_SOCKADDR_SA_LEN
354
+	ifsun.sun_len=len;
355
+#endif
356
+	s=socket(PF_UNIX, type, 0);
357
+	if (s==-1){
358
+		fprintf(stderr, "ERROR: connect_unix_sock: cannot create unix socket"
359
+				" %s: %s [%d]\n", name, strerror(errno), errno);
360
+		goto error;
361
+	}
362
+	if (type==SOCK_DGRAM){
363
+		/* we must bind so that we can receive replies */
364
+		if (reply_socket==0){
365
+			if (sock_dir==0)
366
+				sock_dir="/tmp";
367
+retry:
368
+			ret=snprintf(mysun.sun_path, UNIX_PATH_MAX, "%s/" NAME "_%d",
369
+							sock_dir, rand()); 
370
+			if ((ret<0) ||(ret>=UNIX_PATH_MAX)){
371
+				fprintf(stderr, "ERROR: buffer overflow while trying to"
372
+							"generate unix datagram socket name");
373
+				goto error;
374
+			}
375
+		}else{
376
+			if (strlen(reply_socket)>UNIX_PATH_MAX){
377
+				fprintf(stderr, "ERROR: buffer overflow while trying to"
378
+							"use the provided unix datagram socket name (%s)",
379
+							reply_socket);
380
+				goto error;
381
+			}
382
+			strcpy(mysun.sun_path, reply_socket);
383
+		}
384
+		mysun.sun_family=AF_UNIX;
385
+		if (bind(s, (struct sockaddr*)&mysun, sizeof(mysun))==-1){
386
+			if (errno==EADDRINUSE && (reply_socket==0) && (retries < 10)){
387
+				retries++;
388
+				/* try another one */
389
+				goto retry;
390
+			}
391
+			fprintf(stderr, "ERROR: could not bind the unix socket to"
392
+					" %s: %s (%d)\n",
393
+					mysun.sun_path, strerror(errno), errno);
394
+			goto error;
395
+		}
396
+		unix_socket=mysun.sun_path;
397
+	}
398
+	if (connect(s, (struct sockaddr *)&ifsun, sizeof(ifsun))==-1){
399
+		fprintf(stderr, "ERROR: connect_unix_sock: connect(%s): %s [%d]\n",
400
+				name, strerror(errno), errno);
401
+		goto error;
402
+	}
403
+	return s;
404
+error:
405
+	if (s!=-1) close(s);
406
+	return -1;
407
+}
408
+
409
+
410
+
411
+int connect_tcpudp_socket(char* address, int port, int type)
412
+{
413
+	struct sockaddr_in addr;
414
+	struct hostent* he;
415
+	int sock;
416
+	
417
+	sock=-1;
418
+	/* resolve destination */
419
+	he=gethostbyname(address);
420
+	if (he==0){
421
+		fprintf(stderr, "ERROR: could not resolve %s\n", address);
422
+		goto error;
423
+	}
424
+	/* open socket*/
425
+	addr.sin_family=he->h_addrtype;
426
+	addr.sin_port=htons(port);
427
+	memcpy(&addr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
428
+	
429
+	sock = socket(he->h_addrtype, type, 0);
430
+	if (sock==-1){
431
+		fprintf(stderr, "ERROR: socket: %s\n", strerror(errno));
432
+		goto error;
433
+	}
434
+	if (connect(sock, (struct sockaddr*) &addr, sizeof(struct sockaddr))!=0){
435
+		fprintf(stderr, "ERROR: connect: %s\n", strerror(errno));
436
+		goto error;
437
+	}
438
+	return sock;
439
+error:
440
+	if (sock!=-1) close(sock);
441
+	return -1;
442
+}
443
+
444
+
445
+
446
+static void hexdump(unsigned char* buf, int len, int ascii)
447
+{
448
+	int r, i;
449
+	
450
+	/* dump it in hex */
451
+	for (r=0; r<len; r++){
452
+		if ((r) && ((r%16)==0)){
453
+			if (ascii){
454
+				putchar(' ');
455
+				for (i=r-16; i<r; i++){
456
+					if (isprint(buf[i]))
457
+						putchar(buf[i]);
458
+					else
459
+						putchar('.');
460
+				}
461
+			}
462
+			putchar('\n');
463
+		}
464
+		printf("%02x ", buf[r]);
465
+	};
466
+	if (ascii){
467
+		for (i=r;i%16; i++)
468
+			printf("   ");
469
+		putchar(' ');
470
+		for (i=16*(r/16); i<r; i++){
471
+			if (isprint(buf[i]))
472
+				putchar(buf[i]);
473
+			else
474
+				putchar('.');
475
+		}
476
+	}
477
+	putchar('\n');
478
+}
479
+
480
+
481
+
482
+/* returns: -1 on error, number of bytes written on success */
483
+static int send_binrpc_cmd(int s, struct binrpc_cmd* cmd, int cookie)
484
+{
485
+	struct iovec v[IOVEC_CNT];
486
+	int r;
487
+	unsigned char msg_body[MAX_BODY_SIZE];
488
+	unsigned char msg_hdr[BINRPC_MAX_HDR_SIZE];
489
+	struct binrpc_pkt body;
490
+	int ret;
491
+	int n;
492
+	
493
+	ret=binrpc_init_pkt(&body, msg_body, MAX_BODY_SIZE);
494
+	if (ret<0) goto binrpc_err;
495
+	ret=binrpc_addstr(&body, cmd->method, strlen(cmd->method));
496
+	if (ret<0) goto binrpc_err;
497
+	for (r=0; r<cmd->argc; r++){
498
+		switch(cmd->argv[r].type){
499
+			case BINRPC_T_STR:
500
+				ret=binrpc_addstr(&body, cmd->argv[r].u.strval.s,
501
+										cmd->argv[r].u.strval.len);
502
+				break;
503
+			case BINRPC_T_INT:
504
+				ret=binrpc_addint(&body, cmd->argv[r].u.intval);
505
+				break;
506
+			case BINRPC_T_DOUBLE:
507
+				ret=binrpc_adddouble(&body, cmd->argv[r].u.fval);
508
+				break;
509
+			default:
510
+				fprintf(stderr, "ERROR: unsupported type %d\n",
511
+								cmd->argv[r].type);
512
+		}
513
+		if (ret<0) goto binrpc_err;
514
+	}
515
+	ret=binrpc_build_hdr(BINRPC_REQ, binrpc_pkt_len(&body), cookie, msg_hdr,
516
+							BINRPC_MAX_HDR_SIZE);
517
+	if (ret<0) goto binrpc_err;
518
+	v[0].iov_base=msg_hdr;
519
+	v[0].iov_len=ret;
520
+	v[1].iov_base=msg_body;
521
+	v[1].iov_len=binrpc_pkt_len(&body);
522
+write_again:
523
+	if ((n=writev(s, v, 2))<0){
524
+		if (errno==EINTR)
525
+			goto write_again;
526
+		goto error_send;
527
+	}
528
+	
529
+	return n;
530
+error_send:
531
+	return -1;
532
+binrpc_err:
533
+	return -2;
534
+}
535
+
536
+
537
+
538
+/* reads the whole reply
539
+ * returns < 0 on error, reply size on success + initializes in_pkt */
540
+static int get_reply(int s, unsigned char* reply_buf, int max_reply_size,
541
+						int cookie, struct binrpc_parse_ctx* in_pkt,
542
+						unsigned char** body)
543
+{
544
+	unsigned char* crt;
545
+	unsigned char* hdr_end;
546
+	unsigned char* msg_end;
547
+	int n;
548
+	int ret;
549
+	
550
+	
551
+	hdr_end=crt=reply_buf;
552
+	msg_end=reply_buf+max_reply_size;
553
+	do{
554
+		n=read(s, crt, (int)(msg_end-crt));
555
+		if (n<0){
556
+			if (errno==EINTR)
557
+				continue;
558
+			goto error_read;
559
+		}
560
+		if (verbose >= 3){
561
+			/* dump it in hex */
562
+			printf("received %d bytes in reply (@offset %d):\n",
563
+					n, (int)(crt-reply_buf));
564
+			hexdump(crt, n, 1);
565
+		}
566
+		crt+=n;
567
+		/* parse header if not parsed yet */
568
+		if (hdr_end==reply_buf){
569
+			hdr_end=binrpc_parse_init(in_pkt, reply_buf, n, &ret);
570
+			if (ret<0){
571
+				if (ret==E_BINRPC_MORE_DATA)
572
+					continue;
573
+				goto error_parse;
574
+			}
575
+			if (verbose>1){
576
+				printf("new packet: type %02x, len %d, cookie %02x\n",
577
+						in_pkt->type, in_pkt->tlen, in_pkt->cookie);
578
+			}
579
+			if (in_pkt->cookie!=cookie){
580
+				fprintf(stderr, "bad reply, cookie doesn't match: sent %02x "
581
+						"and received  %02x\n",
582
+						cookie, in_pkt->cookie);
583
+				goto error;
584
+			}
585
+			msg_end=hdr_end+in_pkt->tlen;
586
+			if ((int)(msg_end-reply_buf)>max_reply_size)
587
+				goto error_toolong;
588
+		}
589
+	}while(crt<msg_end);
590
+	
591
+	*body=hdr_end;
592
+	return (int)(msg_end-reply_buf);
593
+error_read:
594
+	return -1;
595
+error_parse:
596
+	return -2;
597
+error:
598
+	return -3;
599
+error_toolong:
600
+	return -4;
601
+}
602
+
603
+
604
+
605
+/* returns a malloced copy of str, with all the escapes ('\') resolved */
606
+static char* str_escape(char* str)
607
+{
608
+	char* n;
609
+	char* ret;
610
+	
611
+	ret=n=malloc(strlen(str)+1);
612
+	if (n==0)
613
+		goto end;
614
+	
615
+	for(;*str;str++){
616
+		*n=*str;
617
+		if (*str=='\\'){
618
+			switch(*(str+1)){
619
+				case 'n':
620
+					*n='\n';
621
+					str++;
622
+					break;
623
+				case 'r':
624
+					*n='\r';
625
+					str++;
626
+					break;
627
+				case 't':
628
+					*n='\t';
629
+					str++;
630
+					break;
631
+				case '\\':
632
+					str++;
633
+					break;
634
+			}
635
+		}
636
+		n++;
637
+	}
638
+	*n=*str; /* terminating 0 */
639
+end:
640
+	return ret;
641
+}
642
+
643
+
644
+
645
+/* parses strings like "bla bla %v 10%% %v\n test=%v",
646
+ * and stops at each %v,  returning  a pointer after the %v, setting *size
647
+ * to the string length (not including %v) and *type to the corresponding
648
+ * BINRPC type (for now only BINRPC_T_ALL).
649
+ * To escape a '%', use "%%", and check for type==-1 (which means skip an call
650
+ *  again parse_fmt).
651
+ * Usage:
652
+ *        n="test: %v,%v,%v\n";
653
+ *        while(*n){
654
+ *          s=n;
655
+ *          n=parse_fmt(n, &type, &size);
656
+ *          printf("%.*s", size, s);
657
+ *          if (type==-1)
658
+ *            continue;
659
+ *          else 
660
+ *             printf("now we should get & print an object of type %d\n", type)
661
+ *        }
662
+ */
663
+static char* parse_fmt(char* fmt, int* type, int* size)
664
+{
665
+	char* s;
666
+
667
+	s=fmt;
668
+	do{
669
+		for(;*fmt && *fmt!='%'; fmt++);
670
+		if (*fmt=='%'){
671
+			switch(*(fmt+1)){
672
+				case 'v':
673
+					*type=BINRPC_T_ALL;
674
+					*size=(int)(fmt-s);
675
+					return (fmt+2);
676
+					break;
677
+				case '%':
678
+					/* escaped % */
679
+					*size=(int)(fmt-s)+1;
680
+					*type=-1; /* skip */
681
+					return (fmt+2);
682
+					break;
683
+			}
684
+		}
685
+	}while(*fmt);
686
+	*type=-1; /* no value */
687
+	*size=(fmt-s);
688
+	return fmt;
689
+}
690
+
691
+
692
+
693
+static int print_body(struct binrpc_parse_ctx* in_pkt, 
694
+						unsigned char* body, int size, char* fmt)
695
+{
696
+	
697
+	unsigned char* p;
698
+	unsigned char* end;
699
+	struct binrpc_val val;
700
+	int ret;
701
+	int rec;
702
+	char *f;
703
+	char* s;
704
+	int f_size;
705
+	int fmt_has_values;
706
+	
707
+	p=body;
708
+	end=p+size;
709
+	rec=0;
710
+	f=fmt;
711
+	fmt_has_values=0;
712
+	/* read body */
713
+	while(p<end){
714
+		if (f){
715
+			
716
+			do{
717
+				if (*f==0)
718
+					f=fmt; /* reset */
719
+				s=f;
720
+				f=parse_fmt(f, &val.type, &f_size);
721
+				printf("%.*s", f_size, s);
722
+				if (val.type!=-1){
723
+					fmt_has_values=1;
724
+					goto read_value;
725
+				}
726
+			}while(*f || fmt_has_values);
727
+			val.type=BINRPC_T_ALL;
728
+		}else{
729
+			val.type=BINRPC_T_ALL;
730
+		}
731
+read_value:
732
+		val.name.s=0;
733
+		val.name.len=0;
734
+		p=binrpc_read_record(in_pkt, p, end, &val, &ret);
735
+		if (ret<0){
736
+			if (fmt)
737
+				putchar('\n');
738
+			/*if (ret==E_BINRPC_MORE_DATA)
739
+				goto error_read_again;*/
740
+			if (ret==E_BINRPC_EOP){
741
+				printf("end of message detected\n");
742
+				break;
743
+			}
744
+			fprintf(stderr, "ERROR while parsing the record %d,"
745
+					" @%d: %02x : %s\n", rec,
746
+					in_pkt->offset, *p, binrpc_error(ret));
747
+			goto error;
748
+		}
749
+		rec++;
750
+		if (fmt){
751
+			print_binrpc_val(&val, 0);
752
+		}else{
753
+			print_binrpc_val(&val, in_pkt->in_struct+in_pkt->in_array);
754
+			putchar('\n');
755
+		}
756
+	}
757
+	if (fmt && *f){
758
+		/* print the rest, with empty values */
759
+		while(*f){
760
+			s=f;
761
+			f=parse_fmt(f, &val.type, &f_size);
762
+			printf("%.*s", f_size, s);
763
+		}
764
+	}
765
+	return 0;
766
+error:
767
+	return -1;
768
+/*error_read_again:
769
+	fprintf(stderr, "ERROR: more data needed\n");
770
+	return -2;
771
+	*/
772
+}
773
+
774
+
775
+
776
+static int print_fault(struct binrpc_parse_ctx* in_pkt, 
777
+						unsigned char* body, int size)
778
+{
779
+	printf("error: ");
780
+	return print_body(in_pkt, body, size, "%v - %v\n");
781
+}
782
+
783
+
784
+
785
+static int run_binrpc_cmd(int s, struct binrpc_cmd * cmd, char* fmt)
786
+{
787
+	int cookie;
788
+	unsigned char reply_buf[MAX_REPLY_SIZE];
789
+	unsigned char* msg_body;
790
+	struct binrpc_parse_ctx in_pkt;
791
+	int ret;
792
+	
793
+	cookie=gen_cookie();
794
+	if ((ret=send_binrpc_cmd(s, cmd, cookie))<0){
795
+		if (ret==-1) goto error_send;
796
+		else goto binrpc_err;
797
+	}
798
+	/* read reply */
799
+	memset(&in_pkt, 0, sizeof(in_pkt));
800
+	if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt,
801
+					&msg_body))<0){
802
+		switch(ret){
803
+			case -1:
804
+				goto error_read;
805
+			case -2:
806
+			case -3:
807
+			case -4:
808
+				goto error_parse;
809
+				goto error_parse;
810
+				goto error_parse;
811
+		}
812
+		goto error;
813
+	}
814
+	switch(in_pkt.type){
815
+		case BINRPC_FAULT:
816
+			if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){
817
+				goto error;
818
+			}
819
+			break;
820
+		case BINRPC_REPL:
821
+			if (print_body(&in_pkt, msg_body, in_pkt.tlen, fmt)<0){
822
+				goto error;
823
+			}
824
+			break;
825
+		default:
826
+			fprintf(stderr, "ERROR: not a reply\n");
827
+			goto error;
828
+	}
829
+	if (verbose) printf(".\n");
830
+	/* normal exit */
831
+	return 0;
832
+binrpc_err:
833
+	fprintf(stderr, "ERROR while building the packet: %s\n", 
834
+				binrpc_error(ret));
835
+	goto error;
836
+error_parse:
837
+	fprintf(stderr, "ERROR while parsing the reply: %s\n", 
838
+				binrpc_error(ret));
839
+	goto error;
840
+error_send:
841
+	fprintf(stderr, "ERROR: send packet failed: %s (%d)\n",
842
+			strerror(errno), errno);
843
+	goto error;
844
+error_read:
845
+	fprintf(stderr, "ERROR: read reply failed: %s (%d)\n",
846
+			strerror(errno), errno);
847
+	goto error;
848
+error:
849
+	return -1;
850
+}
851
+
852
+
853
+
854
+static int parse_line(struct binrpc_cmd* cmd, char* line)
855
+{
856
+	char* p;
857
+	int count;
858
+	
859
+	cmd->method=strtok(line, " \t");
860
+	if (cmd->method==0)
861
+		goto error_no_method;
862
+	count=0;
863
+	for(p=strtok(0, " \t"); p; p=strtok(0, " \t")){
864
+		if (count>=MAX_BINRPC_ARGS)
865
+			goto error_too_many;
866
+		if (parse_arg(&cmd->argv[count], p)<0){
867
+			goto error_arg;
868
+		}
869
+		count++;
870
+	}
871
+	cmd->argc=count;
872
+	return 0;
873
+error_no_method:
874
+	printf( "ERROR: no method name\n");
875
+	return -1;
876
+error_too_many:
877
+	printf("ERROR: too many arguments (%d), no more than %d allowed\n",
878
+			count, MAX_BINRPC_ARGS);
879
+	return -1;
880
+error_arg:
881
+	printf("ERROR: bad argument %d: %s\n", count+1, p);
882
+	return -1;
883
+}
884
+
885
+
886
+
887
+/* resolves builtin aliases */
888
+static void fix_cmd(struct binrpc_cmd* cmd, char** format)
889
+{
890
+	int r;
891
+	
892
+	for (r=0; cmd_aliases[r].name; r++){
893
+		if (strcmp(cmd_aliases[r].name, cmd->method)==0){
894
+			cmd->method=cmd_aliases[r].method;
895
+			if (*format==0)
896
+				*format=cmd_aliases[r].format;
897
+			break;
898
+		}
899
+	}
900
+}
901
+
902
+
903
+
904
+/* intercept builtin commands, returns 1 if intercepted, 0 if not, <0 on error
905
+ */
906
+static int run_builtins(int s, struct binrpc_cmd* cmd)
907
+{
908
+	int r;