This repository has been archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.c
220 lines (184 loc) · 4.8 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/file.h>
#include <err.h>
#include "abbr.h"
/* Overall program structure:
wire.c underlying wire protocols, packet send/recv
query.c basic commands operating on raw OIDs
show.c entry point for formatted CnS output
main.c entry point for commands
oid*.c display rountines for individual OIDs
cmd*.c commands (gps, sms and such)
Grouping routines by topic only (gps.c, sms.c etc) sounds like a good idea
at first, but it turns out there's literaly nothing in common between show*
and the commands operating on the same oids. */
int signalled = -1;
int opt_H = 0; /* dump raw HIP packets */
int opt_C = 0; /* dump raw CnS packets */
int opt_O = 0; /* dump outgoing packets */
int opt_c = 0; /* dump unrecognized CnS packets */
int opt_n = 0; /* show NE/ND packets */
int opt_a = 0; /* show stray CnS packets */
int opt_e = 0; /* show errors */
extern int hipfd;
static int lookslikeoid(char* str);
extern int cmd_(void);
extern int cmd_query(int argc, char** argv);
static void sigsetup(void);
extern int findsierra(int len, char* buf);
#undef CMD$
#define CMD$(cmd, sub, arg, func) extern int func(int argc, char** argv);
#include "cmd.hh"
struct cmd {
char* pri;
char* sub;
int arg;
int (*func)(int argc, char** argv);
} cmds[] = {
#undef CMD$
#define CMD$(cmd, sub, arg, func) { cmd, sub, arg, func },
#include "cmd.hh"
{ NULL, NULL }
};
/* OID commands start with 4 hex digits, possibly followed by query
type char and payload. Anything else is a regular command to be
checked against cmds[]. The source of confusion here are numbers
like "aaaa" (oid) and "aaa" (command, should be "0aaa" instead). */
static int lookslikeoid(char* str)
{
int i;
char c;
for(i = 0; (c = str[i]) && (i < 4); i++)
if((c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F'))
continue;
else
return 0;
if(i < 4)
return 0;
switch(c) {
case ':':
case '=':
case '+':
case '-':
case '\0':
return 1;
default:
return 0;
}
}
/* Some additional care is needed here to match subcommands and produce
readable error messages. The entries in cmds[] are ordered so that
raw command comes after any of its subcommands, allowing a one-pass
scan over cmds[]. */
int trycmds(int argc, char** argv)
{
struct cmd* cmd;
int primatch = 0;
for(cmd = cmds; cmd->pri; cmd++) {
if(strcmp(cmd->pri, *argv))
continue;
else
primatch = 1;
if(!cmd->sub)
break;
if(argc < 2)
continue;
if(strcmp(cmd->sub, *(argv+1)))
continue;
break;
} if(!cmd->pri) {
if(primatch && argc > 1)
errx(5, "unknown %s subcommand %s", *argv, *(argv+1));
else if(primatch)
errx(5, "command %s needs subcommand", *argv);
else
errx(5, "unknown command %s", *argv);
}
return cmd->func(argc - 1, argv + 1);
}
/* The port must be switched to raw mode; sierra.ko makes it a regular
serial port with terminal-style line discipline, completely unsuitable
for a protocol operating in 0x7E-delimited packets instead of lines. */
int modemsetup(int fd)
{
struct termios tio = {
.c_iflag = IGNBRK | IGNPAR,
.c_oflag = 0,
.c_cflag = CLOCAL,
.c_lflag = 0
};
memset(tio.c_cc, 0, sizeof(tio.c_cc));
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
if(tcsetattr(fd, TCSANOW, &tio))
err(4, "tcsetattr");
return 0;
}
static void sighandler(int sig)
{
switch(sig) {
case SIGINT:
if(signalled)
_exit(0);
else
signalled = 1;
break;
}
}
static void sigsetup(void)
{
struct sigaction sa = {
.sa_handler = sighandler,
.sa_mask = { { 0 } },
.sa_flags = 0 /* do NOT auto-resetart read() calls */
};
if(sigaction(SIGINT, &sa, NULL))
err(1, "sigaction");
if(sigaction(SIGALRM, &sa, NULL))
err(1, "sigaction");
}
int main(int argc, char** argv)
{
char* modem = NULL;
int mdlen = 100;
int c;
int r;
while((c = getopt(argc, argv, "+cCHOnad:")) > 0)
switch(c) {
case 'a': opt_a = 1; break;
case 'c': opt_c = 1; break;
case 'C': opt_C = 1; break;
case 'H': opt_H = 1; break;
case 'O': opt_O = 1; break;
case 'n': opt_n = 1; break;
case 'd': modem = optarg; break;
case '?': return -1;
}
if(!modem && findsierra(mdlen, modem = alloca(mdlen)))
errx(2, "can't find suitable device");
if((hipfd = open(modem, O_RDWR)) < 0)
err(3, "can't open %s", modem);
if(flock(hipfd, LOCK_EX | LOCK_NB))
err(3, "can't lock %s", modem);
modemsetup(hipfd);
sigsetup();
argv += optind;
argc -= optind;
optind = 0;
if(argc <= 0)
errx(5, "command required");
if(lookslikeoid(*argv))
r = cmd_query(argc, argv);
else
r = trycmds(argc, argv);
return r;
}