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
|
#include <stdio.h>
#include <gio/gio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/time.h>
#include <glib.h>
#include <glib/gprintf.h>
#include "response.h"
#include "version.h"
static int port = 3333;
#define BUFSIZE 1024
GMainContext *context=NULL;
GMainLoop *mainLoop=NULL;
GSocketConnection * connection = NULL;
GSocketClient * client = NULL;
char exitTokens[5][7] = {"quit", "exit", "logout", "logoff", "local"};
//helper function to nicely terminate the client
static void terminate(char *message, int exitCode)
{
g_object_unref(client);
g_object_unref(connection);
if (message) {
g_print("%s\n", message);
}
exit(exitCode);
}
//a sort of strncmp but with \n or \r exception
static gboolean Matches(char *src, char* dst, gssize size)
{
for(; *src != '\0', (size--)>0; src++) {
if(g_ascii_toupper(*src) != g_ascii_toupper(*dst)) {
return FALSE;
}
dst++;
}
for(; *dst != '\0'; dst++)
if (*dst != '\r' && *dst != '\n') {
return FALSE;
}
return TRUE;
}
#define TIMEOUT 100000 // 100000 useconds, 100ms
#define ONESECONDINUSEC 1000000
//GIOChannel callback
static gboolean
stdinAvailable (GIOChannel *source, GIOCondition condition, gpointer data)
{
char tmp[BUFSIZE];
memset(tmp,0,BUFSIZE);
int size = read(0, tmp, BUFSIZE);
if(size<=0) {
return TRUE;
}
// Ignore second \n if two are sent in rapid succession.
// This happens when the serial input is terminated with \r\n,
// which getty translates to \n\n. The IO handler splits this
// into 2 input lines, so we can't use a simple regex to
// replace it.
static struct timeval last = { 0, 0 }; // the time this last ran
static struct timeval now; // the time now
gettimeofday(&now, NULL );
if (last.tv_sec != 0) { // shouldn't be zero if we have been in here before
if (strcmp(tmp, "\n") == 0) { // is this a newline all on it's own?
if (last.tv_sec == now.tv_sec) {
// if we're in the same second we can just check the usec field
if (now.tv_usec - last.tv_usec < TIMEOUT) {
// ignore this \n
return TRUE;
}
}
// - check that last and now sec fields are only one second apart
// - check that the time elapsed in this second is smaller than the
// timeout, if it isn't we don't need to check the other second
// - check that there is less of the last second remaining than the timeout
else if (now.tv_sec - last.tv_sec == 1 && now.tv_usec < TIMEOUT && (ONESECONDINUSEC - last.tv_usec) < TIMEOUT) {
// and then check the the time left in the last second
// plus the time in this second is less than the timeout
if ((ONESECONDINUSEC - last.tv_usec) + now.tv_usec < TIMEOUT) {
// ignore this \n
return TRUE;
}
}
}
}
last = now;
//test if we have an exit word
int idx=0;
for(idx=0; idx<sizeof(exitTokens)/sizeof(exitTokens[0]); idx++) {
if(Matches(exitTokens[idx], tmp, strlen(exitTokens[idx]))) {
g_socket_close(g_socket_connection_get_socket(connection), NULL);
terminate("Goodbye", 0);
}
}
//write the message
gssize written=0;
GPollableOutputStream* out = (GPollableOutputStream*)data;
if (g_pollable_output_stream_is_writable(out) == FALSE) {
g_print("stream is not writable\n");
}
GError* error = NULL;
written = g_pollable_output_stream_write_nonblocking(out, tmp, size, NULL, &error);
if(error != NULL && error->message) {
g_print("Got error: %s\n", error->message);
}
if (written != size || written <= 0) {
g_print("Could not write. blocking. Written: %d\n", written);
return FALSE;
}
return TRUE;
}
//callback for server input
static gboolean cbServerInput(gpointer data, gpointer additional)
{
char buffer[BUFSIZE];
gssize size=0;
memset(buffer,0,BUFSIZE);
GPollableInputStream* inStream = (GPollableInputStream*)data;
GError *error = NULL;
size=g_pollable_input_stream_read_nonblocking(inStream, buffer, BUFSIZE, NULL, &error);
if(size <=0) {
terminate("Connection to server lost", -1);
}
if(buffer[0] != ' ') {
g_print("%s", buffer);
}
return TRUE;
}
int main(int argc, char** argv)
{
/* create a new connection */
g_type_init ();
GOutputStream *out;
GInputStream *in;
GError* error = NULL;
GIOChannel* stdinChannel = NULL;
client = g_socket_client_new();
/* connect to the host */
connection = g_socket_client_connect_to_host (client,
(gchar*)"localhost",
port,
NULL,
&error);
if (error != NULL) {
g_print("Could not connect to server on port %d\n", port);
g_object_unref(client);
return -1;
}
g_printf("\r\nWelcome! Avtech Electrosystems Ltd. - Firmware v%s\r\n\r\n",FW_VERSION);
g_print("> ");
//register Pollable sources
out = g_io_stream_get_output_stream (G_IO_STREAM (connection));
in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
GSource *outSource = NULL;
GSource *inSource = g_pollable_input_stream_create_source((struct GPollableInputStream*)in, NULL);
outSource = g_pollable_output_stream_create_source((struct GPollableOutputStream*)out, NULL);
//register stdin channel
stdinChannel = g_io_channel_unix_new(0);
if(stdinChannel == NULL) {
terminate("Could not open STDIN channel", -2);
}
//attach the sources
g_io_add_watch(stdinChannel, G_IO_IN, stdinAvailable, (struct GPollableOutputStream*)out);
g_source_set_callback(inSource, cbServerInput, (GPollableOutputStream*)in, NULL);
g_source_attach(inSource, NULL);
g_main_loop_run (g_main_loop_new (NULL, FALSE));
return 0;
}
|