nmc-utils  0.1.1
nmrun.c
Go to the documentation of this file.
1 /*
2  * libEasyNMC DSP communication library.
3  * Copyright (C) 2014 RC "Module"
4  * Written by Andrew 'Necromant' Andrianov <andrew@ncrmnt.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * ut WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  */
21 
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <sys/ioctl.h>
25 #include <unistd.h>
26 #include <sys/select.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <termios.h>
32 #include <fcntl.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdint.h>
38 #include <getopt.h>
39 #include <errno.h>
40 #include <sys/epoll.h>
41 
42 int g_debug = 1;
43 int g_force = 0;
44 int g_nostdio = 0;
45 int g_detach = 0;
46 int g_nosigint = 0;
47 
48 struct easynmc_handle *g_handle = NULL;
49 
50 static uint32_t entrypoint;
51 
52 #define dbg(fmt, ...) if (g_debug) { \
53  fprintf(stderr, "libeasynmc: " fmt, ##__VA_ARGS__); \
54  }
55 
56 #define err(fmt, ...) if (g_debug) { \
57  fprintf(stderr, "libeasynmc: " fmt, ##__VA_ARGS__); \
58  }
59 
60 
61 #include <easynmc.h>
62 
63 
64 void usage(char *nm)
65 {
66  fprintf(stderr,
67  "nmrun - The EasyNMC app runner wrapper\n"
68  "(c) 2014 RC Module | Andrew 'Necromant' Andrianov <andrew@ncrmnt.org>\n"
69  "This is free software; see the source for copying conditions. There is NO\n"
70  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
71  "License: LGPLv2 \n"
72  "Usage: %s [options] myapp.abs [arguments] - operate on core 0 (default)\n"
73  "Valid options are: \n"
74  " --help - Show this help\n"
75  " --core=id - Select a core to operate on (Default - use first usused core)\n"
76  " --force - Disable internal seatbelts (DANGEROUS!)\n"
77  " --nostdio - Do not auto-attach stdio\n"
78  " --nosigint - Do not catch SIGINT\n"
79  " --detach - Run app in background (do not attach console)\n"
80  "Debugging options: \n"
81  " --debug - Print lots of debugging info (nmctl)\n"
82  " --debug-lib - Print lots of debugging info (libeasynmc)\n"
83  , nm
84 );
85 }
86 
87 static struct option long_options[] =
88 {
89  /* Generic stuff. */
90  {"help", no_argument, 0, 'h' },
91 
92  /* Options */
93  {"core", required_argument, 0, 'c' },
94  {"force", no_argument, &g_force, 1 },
95  {"nostdio", no_argument, &g_nostdio, 1 },
96  {"nosigint", no_argument, &g_nosigint, 1 },
97  {"detach", no_argument, &g_detach, 1 },
98 
99  /* Debugging hacks */
100  {"debug-lib", no_argument, &g_libeasynmc_debug, 1 },
101  {"debug", no_argument, &g_debug, 1 },
102  {"dump-ldr-regs", optional_argument, 0, 'D' },
103 
104  {0, 0, 0, 0}
105 };
106 
107 void nonblock(int fd, int state)
108 {
109  struct termios ttystate;
110 
111  //get the terminal state
112  tcgetattr(fd, &ttystate);
113  if (state==1)
114  {
115  //turn off canonical mode
116  ttystate.c_lflag &= ~ICANON;
117  ttystate.c_lflag &= ~ECHO;
118  ttystate.c_lflag = 0;
119  ttystate.c_cc[VTIME] = 0; /* inter-character timer unused */
120  ttystate.c_cc[VMIN] = 0; /* We're non-blocking */
121 
122  }
123  else if (state==0)
124  {
125  //turn on canonical mode
126  ttystate.c_lflag |= ICANON | ECHO;
127  }
128  //set the terminal attributes.
129  tcsetattr(fd, TCSANOW, &ttystate);
130 
131 }
132 
133 void die()
134 {
135  fprintf(stderr, "\nCTRL+C pressed, terminating app\n");
136 
137  if (!g_nosigint)
138  easynmc_stop_app(g_handle);
139 
140  if (isatty(STDIN_FILENO))
141  nonblock(STDIN_FILENO, 0);
142  exit(0);
143 }
144 
145 
146 void handle_sigint(int sig)
147 {
148  signal(sig, SIG_IGN);
149  die();
150 }
151 
152 #define NUMEVENTS 3
153 
154 
155 int read_inbound(int fd)
156 {
157  unsigned char fromnmc[1024];
158  do {
159  int n;
160  n = read(fd, fromnmc, 1024);
161  if (n == -1) {
162  if (errno==EAGAIN)
163  break;
164  else {
165  perror("read-from-nmc");
166  return errno;
167  }
168  }
169  write(STDOUT_FILENO, fromnmc, n);
170  } while (1);
171  return 0;
172 }
173 
174 /* DO NOT SAY ANYTHING. Please ;) */
176 {
177  int i;
178  int ret = 1;
179  struct epoll_event event[3];
180  struct epoll_event *events;
181  int efd = epoll_create(2);
182  setvbuf(stdin,NULL,_IONBF,0);
183 
184  if (efd == -1)
185  {
186  perror ("epoll_create");
187  ret = 1;
188  goto errclose;
189  }
190 
191  int flags = fcntl(h->iofd, F_GETFL, 0);
192  fcntl(h->iofd, F_SETFL, flags | O_NONBLOCK);
193 
194  if (!isatty(STDIN_FILENO)) {
195  flags = fcntl(STDIN_FILENO, F_GETFL, 0);
196  fcntl(h->iofd, F_SETFL, flags | O_NONBLOCK);
197  } else {
198  nonblock(STDIN_FILENO, 1);
199  }
200 
201  event[0].data.fd = h->iofd;
202  event[0].events = EPOLLIN | EPOLLOUT | EPOLLET;
203 
204  event[1].data.fd = h->memfd;
205  event[1].events = EPOLLNMI | EPOLLHP | EPOLLET;
206 
207  event[2].data.fd = STDIN_FILENO;
208  event[2].events = EPOLLIN | EPOLLET;
209 
210  for (i = 0; i < 3; i++) {
211  ret = epoll_ctl (efd, EPOLL_CTL_ADD, event[i].data.fd, &event[i]);
212  if (ret == -1)
213  {
214  perror ("epoll_ctl");
215  ret = 1;
216  goto errclose;
217  }
218  }
219 
220  events = calloc (NUMEVENTS, sizeof event);
221 
222  int can_read_stdin = 0;
223  int can_write_to_nmc = 0;
224 
225  int gotfromstdin = 0;
226  int written_to_nmc = 0;
227  unsigned char tonmc[1024];
228 
229  while (1) {
230  int num, i;
231  num = epoll_wait(efd, events, NUMEVENTS, -1);
232  for (i = 0; i < num; i++) {
233  if ((events[i].data.fd == STDIN_FILENO) && (events[i].events & EPOLLIN))
234  can_read_stdin=1;
235 
236  if (can_read_stdin && !gotfromstdin)
237  {
238  gotfromstdin = read(STDIN_FILENO, tonmc, 1024);
239  if (isatty(STDIN_FILENO) && tonmc[0] == 3)
240  die();
241 
242  if (-1 == gotfromstdin) {
243  perror("read-from-stdin");
244  return 1;
245  }
246  }
247 
248  if (events[i].data.fd == h->iofd && (events[i].events & EPOLLIN)) {
249  ret = read_inbound(h->iofd);
250  if (ret != 0)
251  return ret;
252  }
253 
254  if (events[i].data.fd == h->iofd && (events[i].events & EPOLLOUT))
255  can_write_to_nmc++;
256 
257  if ((events[i].data.fd == h->memfd) &&
259  /*
260  * Read any bytes left in circular buffer.
261  */
262  ret = read_inbound(h->iofd);
263  if ( ret != 0)
264  return ret;
265 
266  ret = easynmc_exitcode(h);
267  fprintf(stderr, "App terminated with result %d, exiting\n", ret);
268 
269  return ret;
270  }
271 
272  if (can_write_to_nmc && (written_to_nmc != gotfromstdin)) {
273  int n = write(h->iofd, &tonmc[written_to_nmc],
274  gotfromstdin - written_to_nmc);
275 
276  if (n > 0) {
277  written_to_nmc += n;
278  if (written_to_nmc == gotfromstdin) {
279  gotfromstdin = 0;
280  written_to_nmc = 0;
281  }
282  }
283  else if (errno == EAGAIN) {
284  break;
285  } else {
286  perror("write-to-nmc");
287  return 1;
288  }
289  }
290  }
291  }
292 
293 errclose:
294  easynmc_close(h);
295  return ret;
296 }
297 
298 
299 
300 
301 int main(int argc, char **argv)
302 {
303  int core = 0; // EASYNMC_CORE_ANY; /* Default - use first available core */
304  int ret;
305  char* self = "nmrun";
306 
307  if (argc < 2)
308  usage(argv[0]), exit(1);
309 
310  while (1)
311  {
312  int c;
313  int option_index = 0;
314  c = getopt_long (argc, argv, "c:h",
315  long_options, &option_index);
316 
317  /* Detect the end of the options. */
318  if (c == -1)
319  break;
320 
321  switch (c)
322  {
323  case 'c':
324  if (strcmp(optarg, "all") == 0)
325  core = -1;
326  else
327  core = atoi(optarg);
328  break;
329  case 'h':
330  usage(argv[0]);
331  exit(1);
332  break;
333  case '?':
334  default:
335  break;
336  }
337  }
338 
339  char* absfile = argv[optind++];
340  int num_args = argc - optind;
341  char **args = &argv[optind];
342 
343  uint32_t flags = ABSLOAD_FLAG_DEFAULT;
344 
345  if (g_nostdio)
346  flags &= ~(ABSLOAD_FLAG_STDIO);
347 
348  struct easynmc_handle *h = easynmc_open(core);
349  g_handle = h;
350 
351  if (!h) {
352  fprintf(stderr, "Failed to open core %d\n", core);
353  exit(1);
354  }
355 
356  int state;
357  if ((state = easynmc_core_state(h)) != EASYNMC_CORE_IDLE) {
358  fprintf(stderr, "Core is %s, expecting core to be idle\n", easynmc_state_name(state));
359  exit(1);
360  }
361 
362  ret = easynmc_load_abs(h, absfile, &entrypoint, flags);
363  if (0!=ret) {
364  fprintf(stderr, "Failed to upload abs file\n");
365  exit(1);
366  }
367 
368  ret = easynmc_set_args(h, self, num_args, args);
369  if (ret != 0) {
370  fprintf(stderr, "WARN: Failed to set arguments. Not supported by app?\n");
371  }
372 
373  ret = easynmc_pollmark(h);
374 
375  if (ret != 0) {
376  fprintf(stderr, "Failed to reset polling counter (\n");
377  exit(1);
378  };
379 
380 
381  ret = easynmc_start_app(h, entrypoint);
382  if (ret != 0) {
383  fprintf(stderr, "Failed to start app (\n");
384  exit(1);
385  }
386 
387  ret = 0;
388 
389  if (!g_nosigint)
390  signal(SIGINT, handle_sigint);
391 
393 
394  if (!g_detach) {
395  fprintf(stderr, "Application now started, hit CTRL+C to %s it\n", g_nosigint ? "detach" : "stop");
396  ret = run_interactive_console(h);
397  } else {
398  fprintf(stderr, "Application started, detaching\n");
399  }
400 
401 
402  if (isatty(STDIN_FILENO))
403  nonblock(STDIN_FILENO, 0);
404 
405  return ret;
406 }