1: /*
     2:  *  Guitar-ZyX(tm)::MasterControlProgram - portable guitar F/X controller
     3:  *  Copyright (C) 2009  Douglas McClendon
     4:  *
     5:  *  This program is free software: you can redistribute it and/or modify
     6:  *  it under the terms of the GNU General Public License as published by
     7:  *  the Free Software Foundation, version 3 of the License.
     8:  *
     9:  *  This program is distributed in the hope that it will be useful,
    10:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    11:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12:  *  GNU General Public License for more details.
    13:  *
    14:  *  You should have received a copy of the GNU General Public License
    15:  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16: */
    17: /*
    18: #
    19: #############################################################################
    20: #############################################################################
    21: ##
    22: ## gzmcp: debug client (x86)
    23: ##
    24: #############################################################################
    25: ##
    26: ## Copyright 2007-2009 Douglas McClendon <dmc AT filteredperception DOT org>
    27: ##
    28: #############################################################################
    29: #############################################################################
    30: #
    31: #
    32: #
    33: # usage: gzmcp-client-debug.x86
    34: #
    35: # The Guitar-ZyX Master Control Program Debug Client listens on the localhost
    36: # at the udp port specified by GZMCP_DEF_UDP_PORT or 24642 for a gzmcp
    37: # server to broadcast its presence.  Upon identification of gzmcp server
    38: # a command connection will be initiated with it, and a special command
    39: # syntax entered at the client's stdin, will cause gzmcp commands to be
    40: # sent to the server, just as the real NDS client does.
    41: #
    42: # The acceptable commands are >>>
    43: #
    44: # preset <preset int>
    45: # parameter <parameter int> <value int>
    46: # exit(or quit)
    47: #
    48: # note: for these effectively midi commands, channel 1 is used.
    49: #
    50: */
    51: 
    52: 
    53: 
    54: #include <unistd.h> 
    55: 
    56: #include <sys/types.h>  
    57: 
    58: #include <sys/wait.h> 
    59: 
    60: #include <stdio.h> 
    61: 
    62: #include <stdlib.h> 
    63: 
    64: #include <stdarg.h> 
    65: 
    66: #include <errno.h> 
    67: #include <limits.h> 
    68: 
    69: #include <string.h> 
    70: 
    71: #include <sys/socket.h> 
    72: 
    73: #include <netinet/in.h> 
    74: 
    75: #include <arpa/inet.h> 
    76: 
    77: #include <netdb.h> 
    78: 
    79: #include <math.h> 
    80: 
    81: #include <alsa/asoundlib.h> 
    82: 
    83: #include <signal.h> 
    84: 
    85: 
    86: #include "client.h" 
    87: 
    88: 
    89: 
    90: 
    91: char prog_name[PATH_MAXLEN + 1];
    92: char prog_dir[PATH_MAXLEN + 1];
    93: 
    94: int cmd_socket_fd;
    95: 
    96: long magic_cookie;
    97: 
    98: 
    99: 
   100: int 
   101: main(int argc, char *argv[]) {
   102: 
   103:   // port number specified by the user on the commandline
   104:   long bcast_portnum;
   105:   long cmd_portnum;
   106: 
   107:   // server net address
   108:   // for standard socket address structure 
   109:   struct sockaddr_in server_address;
   110: 
   111: 
   112:   //
   113:   // derived global 'constants'
   114:   //
   115:   dirname(prog_dir, argv[0]);
   116:   basename(prog_name, argv[0]);
   117: 
   118:   //
   119:   // Hello User
   120:   //
   121:   fprintf(stdout, "\n\n\n");
   122:   fprintf(stdout, 
   123: 	  "==================================================\n");
   124:   fprintf(stdout, 
   125: 	  "Guitar-ZyX Master Control Program X86 Debug Client\n");
   126:   fprintf(stdout, 
   127: 	  " - v2k9.dev - (c)2009 Douglas McClendon\n");
   128:   fprintf(stdout, 
   129: 	  "==================================================\n");
   130:   fprintf(stdout, "\n\n\n");
   131:   fprintf(stdout, 
   132: 	  "==================================================\n");
   133:   fprintf(stdout, 
   134: 	  "\nvalid gzmcp commands\n--------------------\n\n");
   135:   fprintf(stdout, 
   136: 	  ">>> set-preset <new_preset(int)>\n\n");
   137:   fprintf(stdout, 
   138: 	  ">>> set-parameter <parameter_id(int)> <value(int)>\n\n");
   139:   fprintf(stdout, 
   140: 	  "==================================================\n");
   141:   fprintf(stdout, "\n\n\n");
   142: 
   143:   //
   144:   // set defaults
   145:   //
   146:   cmd_portnum = GZMCP_DEF_TCP_PORT;
   147:   bcast_portnum = GZMCP_DEF_UDP_PORT;
   148: 
   149:   //
   150:   // parse commandline
   151:   //
   152:   // TODO: add better getopt-ish, with --verbose...
   153:   if (argc > 2) {
   154:     // too many args: tell the user how to use
   155:     usage();
   156:   } else if (argc == 2) {
   157:     // get the user specified port number from the command line arg
   158:     cmd_portnum = ec_strtol(argv[1]);
   159:     bcast_portnum = ec_strtol(argv[1]);
   160:   } else {
   161:     // defaults are fine
   162:   } // end parsing commandline
   163: 
   164:   // put the global handshake cookie where it can be sizeof()d
   165:   magic_cookie = (long)GZMCP_DEF_MAGIC_COOKIE;
   166: 
   167:   //
   168:   // wait_for_bcast: wait for a server broadcast, retrieving the address
   169:   //
   170:   wait_for_bcast(bcast_portnum,
   171: 		 &server_address);
   172: 
   173:   //
   174:   // connect to the server, exchange handshake
   175:   //
   176:   connect_to_server(cmd_portnum,
   177: 		    server_address);
   178: 
   179:   //
   180:   // get_and_handle_user_commands: prompt user for input and handle
   181:   //
   182:   get_and_handle_user_commands();
   183: 
   184:   //
   185:   // network_shutdown: clean up network resources
   186:   //
   187:   network_shutdown();
   188: 
   189:   //
   190:   // done
   191:   //
   192:   return(0);
   193: 
   194: } // end main
   195: 
   196: 
   197: 
   198: 
   199: 
   200: void usage(void) {
   201:   fprintf(stderr, "\n\nusage: gzmcpd [<port number> default=24642]\n\n\n");
   202:   exit(1);
   203: }
   204: 
   205: 
   206: 
   207: long ec_strtol(char *str) {
   208: 
   209:   char *endptr;
   210:   long val;
   211: 
   212:   // error check strtol 
   213:   // note: code verbatim from man strtol
   214:   // To distinguish success/failure after call 
   215:   errno = 0;    
   216:   val = strtol(str, &endptr, 10);
   217:     
   218:   // Check for various possible errors 
   219:   if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
   220:       || (errno != 0 && val == 0)) {
   221:     perror("strtol");
   222:     exit(EXIT_FAILURE);
   223:   }
   224:     
   225:   if (endptr == str) {
   226:     fprintf(stderr, "No digits were found\n");
   227:     exit(EXIT_FAILURE);
   228:   }
   229:   
   230:   // If we got here, strtol() successfully parsed a number 
   231:   return val;
   232: }
   233: 
   234: 
   235: 
   236: void basename(char *dest, char *path) {
   237: 
   238:   char *new_result;
   239:   char *last_result;
   240: 
   241: 
   242:   // look for the last '/'
   243:   last_result = path;
   244:   while ((new_result = strstr(last_result, "/")) != NULL) {
   245:     last_result = new_result + 1;
   246:   }
   247: 
   248:   // copy to dest, everything after the last '/'
   249:   strncpy(dest, last_result, PATH_MAXLEN);
   250: 
   251:   // guarantee null terminated destination string
   252:   dest[PATH_MAXLEN] = 0;
   253: 
   254:   return;
   255: }
   256: 
   257: 
   258: 
   259: void dirname(char *dest, char *path) {
   260: 
   261:   char *new_result;
   262:   char *last_result;
   263:   int dirname_length;
   264: 
   265: 
   266:   // look for the last '/'
   267:   last_result = path;
   268:   while ((new_result = strstr(last_result, "/")) != NULL) {
   269:     last_result = new_result + 1;
   270:   }
   271: 
   272:   // calculdate the length of the result we want
   273:   dirname_length = strlen(path) - strlen(last_result) - 1;
   274: 
   275:   if (dirname_length > PATH_MAXLEN) {
   276:     die("dirname() result greater than PATH_MAXLEN - %d\n",
   277: 	PATH_MAXLEN);
   278:   }
   279: 
   280:   // copy to dest, everything up to last '/'
   281:   strncpy(dest, 
   282: 	  path, 
   283: 	  dirname_length);
   284: 
   285:   // guarantee null terminated destination string
   286:   dest[PATH_MAXLEN] = 0;
   287: 
   288:   return;
   289: }
   290: 
   291: 
   292: 
   293: void status(const char *msg_fmt, ...) {
   294: 
   295:   char print_msg[STATUS_MSG_MAXLEN + 1];
   296: 
   297:   va_list msg_arg;
   298: 
   299: 
   300:   // standard printf style variable argument processing...
   301:   va_start(msg_arg, msg_fmt);
   302:   vsnprintf(print_msg, 
   303: 	    STATUS_MSG_MAXLEN,
   304:             msg_fmt, 
   305: 	    msg_arg);
   306:   va_end(msg_arg);
   307: 
   308:   fprintf(stdout, 
   309: 	  "%s: %s\n", 
   310: 	  prog_name,
   311: 	  print_msg);
   312: }
   313: 
   314: 
   315: 
   316: void die(const char *msg_fmt, ...) {
   317: 
   318:   char print_msg[STATUS_MSG_MAXLEN + 1];
   319: 
   320:   va_list msg_arg;
   321: 
   322: 
   323:   // standard printf style variable argument processing...
   324:   va_start(msg_arg, msg_fmt);
   325:   vsnprintf(print_msg, 
   326: 	    STATUS_MSG_MAXLEN,
   327:             msg_fmt, 
   328: 	    msg_arg);
   329:   va_end(msg_arg);
   330: 
   331:   fprintf(stdout, 
   332: 	  "%s: FATAL ERROR: %s\n", 
   333: 	  prog_name,
   334: 	  print_msg);
   335:   exit(1);
   336: 
   337: }
   338: 
   339: 
   340: 
   341: void wait_for_bcast(int portnum,
   342: 		    struct sockaddr_in* address) {
   343: 
   344:   // file descriptor for the server brodcast udp socket
   345:   int bcast_socket_fd;
   346: 
   347:   int found_server = 0;
   348: 
   349:   int bytes_received;
   350:   unsigned int from_length;
   351: 
   352:   // for setsockopt
   353:   int optval;
   354: 
   355:   // buffer to receive broadcast messages
   356:   char beacon_msg[GZMCP_BEACON_MSG_MAXLEN + 1];
   357: 
   358: 
   359:   status("listening for server broadcasts on udp port %d\n",
   360: 	 portnum);
   361: 
   362:   status("listening for magic cookie - 0x%08x\n",
   363: 	 magic_cookie);
   364: 
   365:   // create the broadcast reception udp socket
   366:   //   - AF_INET means ipv4, vs AF_INET6 vs AF_UNIX/AF_LOCAL
   367:   //   - SOCK_DGRAM means udp, vs SOCK_STREAM for tcp
   368:   //   - 0 means default protocol type for family
   369:   bcast_socket_fd = socket(AF_INET, 
   370: 			   SOCK_DGRAM,
   371: 			   0);
   372:   if (bcast_socket_fd < 0) {
   373:     perror("socket()");
   374:     die("couldn't create broadcast socket");
   375:   }
   376: 
   377:   // enable broadcast
   378:   // note: perhaps this is unnecessary, since we only receive
   379:   optval = 1;
   380:   if (setsockopt(bcast_socket_fd, 
   381: 		 SOL_SOCKET, 
   382: 		 SO_BROADCAST, 
   383: 		 &optval, sizeof(optval)) < 0) {
   384:     perror("setsockopt()");
   385:     die("failed to set broadcast socket options\n");
   386:   }
   387: 
   388: 
   389:   // create a destination address structure, initialize...
   390:   memset(address, 0, sizeof(*address));
   391: 
   392:   // configure the address structure for our purposes
   393:   // INET, vs e.g. UNIX for local non network sockets
   394:   address->sin_family = AF_INET;
   395:   address->sin_port = htons(portnum);
   396:   // ???
   397:   address->sin_addr.s_addr = htonl(INADDR_ANY);
   398: 
   399:   if (bind(bcast_socket_fd, (struct sockaddr *) address,
   400:     	   sizeof(*address)) < 0) {
   401:     perror("bind()");
   402:     die("failed to bind to broadcast socket");
   403:   }
   404:   
   405:   //
   406:   // listen for broadcasts until a gzmcp server is discovered
   407:   //
   408:   while(!found_server) {
   409: 
   410:     // initialize the length of the address result
   411:     from_length = sizeof(struct sockaddr_in);
   412: 
   413:     bytes_received = recvfrom(bcast_socket_fd,
   414: 			      beacon_msg,
   415: 			      GZMCP_BEACON_MSG_MAXLEN,
   416: 			      0,
   417: 			      (struct sockaddr*)address,
   418: 			      &from_length);
   419:     //    status("debug: bytes_received for beacon was %d\n", 
   420:     //	   bytes_received);
   421:     if (bytes_received < 0) {
   422:       // udp makes no guarantees
   423:       //      perror("sendto()");
   424:       //      status("could not send message\n");
   425:     } else {
   426:       // check if message is a gzmcp server broadcast message
   427:       if (bcmp((void *)beacon_msg, 
   428: 	       (void *)&magic_cookie, 
   429: 	       4) == 0) {
   430: 	// a correct magic cookie
   431: 	status("server at %s had the right magic cookie!\n", 
   432: 	       inet_ntoa(address->sin_addr));
   433: 	found_server = 1;
   434: 
   435:       } else {
   436: 	// not a correct magic cookie
   437: 	status("got an incorrect magic cookie! ignoring...\n");
   438:       }
   439: 
   440:     } // end handling received possible cookie data
   441: 
   442:   } // end while(1)
   443: 
   444: } // end wait_for_bcast()
   445: 
   446: 
   447: 
   448: void do_handshake(int cmd_socket_fd) {
   449: 
   450:   // for read/write return values
   451:   int numbytes;
   452: 
   453:   // for outgoing handshake
   454:   gzmcp_cmd handshake_cmd;
   455:   // for incoming handshake
   456:   gzmcp_cmd reflected_handshake_cmd;
   457: 
   458: 
   459:   // create handshake command/packet
   460:   handshake_cmd.type = GZMCP_CMD_HANDSHAKE;
   461:   memcpy((void *)handshake_cmd.data.handshake.string, 
   462: 	 (void *)&magic_cookie,
   463: 	 sizeof(magic_cookie));
   464:   
   465:   // send the handshake
   466:   numbytes = send(cmd_socket_fd,
   467: 		  (void *)&handshake_cmd,
   468: 		  sizeof(gzmcp_cmd), 
   469: 		  0);
   470:   // check for errors
   471:   if (numbytes < 0) {
   472:     perror("send()");
   473:     die("handshake send");
   474:   }
   475:   
   476:   //  status("debug: handshake sent, numbytes is %d, sizeof handshake cmd is %d\n",
   477:   //	 numbytes, sizeof(gzmcp_cmd));
   478:   if (numbytes == sizeof(gzmcp_cmd)) {
   479:     status("handshake sent\n");
   480:   } else {
   481:     die("failed to send handshake");
   482:   }
   483: 
   484:   // recieve the reciprocal handshake
   485:   numbytes = recv(cmd_socket_fd,
   486: 		  (void *)&reflected_handshake_cmd,
   487: 		  sizeof(gzmcp_cmd), 
   488: 		  0);
   489:   // check for errors
   490:   if (numbytes < 0) {
   491:     perror("recv()");
   492:     die("reflected handshake reception");
   493:   }
   494:   
   495:   //  status("debug: reflected handshake received, numbytes is %d, sizeof handshake is %d\n",
   496:   //	 numbytes, sizeof(gzmcp_cmd));
   497:   
   498:   if (numbytes == sizeof(gzmcp_cmd)) {
   499:     
   500:     // check reflected handshake against the original
   501:     if (memcmp((void*)&reflected_handshake_cmd,
   502: 	       (void*)&handshake_cmd,
   503: 	       sizeof(gzmcp_cmd)) == 0 ) {
   504:       status("correct handshake received\n");
   505:     } else {
   506:       die("incorrect handshake received");
   507:     }
   508: 
   509:   } else {
   510:     // TODO(?) receive rest of partial handshake
   511:     die("received partial handshake back");
   512:   }
   513: 
   514: }
   515: 
   516: 
   517: 
   518: void set_preset(int cmd_socket_fd, 
   519: 		char *preset_string) {
   520: 
   521:   // packet structure for sending commands to the server
   522:   gzmcp_cmd newcmd;
   523: 
   524:   // for send() return values
   525:   int numbytes;
   526: 
   527: 
   528:   // create new command/packet
   529:   newcmd.type = GZMCP_CMD_PRESET;
   530:   newcmd.data.preset.channel = 1;
   531:   newcmd.data.preset.preset = ec_strtol(preset_string);
   532:   
   533:   status("setting preset to %d ...\n", 
   534: 	 newcmd.data.preset.preset);
   535:   
   536:   // send the new command
   537:   numbytes = send(cmd_socket_fd,
   538: 		  (void *)&newcmd,
   539: 		  sizeof(gzmcp_cmd), 
   540: 		  0);
   541:   // check for errors
   542:   if (numbytes < 0) {
   543:     perror("send()");
   544:     die("set-preset command sending failed");
   545:   }
   546:   
   547:   // debug
   548:   status("debug: set-preset sent, numbytes is %d\n",
   549: 	 numbytes);
   550:   
   551:   if (numbytes == sizeof(gzmcp_cmd)) {
   552:     status("set-preset command sent\n");
   553:   } else {
   554:     die("failed to send set-preset command");
   555:   }
   556: 
   557: }
   558: 
   559: 
   560: 
   561: void set_parameter(int cmd_socket_fd, 
   562: 		   char *parameter_string,
   563: 		   char *value_string) {
   564: 
   565:   // packet structure for sending commands to the server
   566:   gzmcp_cmd newcmd;
   567: 
   568:   // for send() return values
   569:   int numbytes;
   570: 
   571: 
   572:   // create new command/packet
   573:   newcmd.type = GZMCP_CMD_PARAMETER;
   574:   newcmd.data.parameter.channel = 1;
   575:   newcmd.data.parameter.parameter = ec_strtol(parameter_string);
   576:   newcmd.data.parameter.value = ec_strtol(value_string);
   577:   
   578:   status("setting parameter %d to value %d ...\n", 
   579: 	 newcmd.data.parameter.parameter,
   580: 	 newcmd.data.parameter.value);
   581:   
   582:   // send the new command
   583:   numbytes = send(cmd_socket_fd,
   584: 		  (void *)&newcmd,
   585: 		  sizeof(gzmcp_cmd), 
   586: 		  0);
   587:   // check for errors
   588:   if (numbytes < 0) {
   589:     perror("send()");
   590:     die("set-parameter command sending failed");
   591:   }
   592:   
   593:   // debug
   594:   status("debug: set-parameter sent, numbytes is %d\n",
   595: 	 numbytes);
   596:   
   597:   if (numbytes == sizeof(gzmcp_cmd)) {
   598:     status("set-parameter command sent\n");
   599:   } else {
   600:     die("failed to send set-parameter command");
   601:   }
   602:       
   603: }
   604: 
   605: 
   606: 
   607: void get_update(int cmd_socket_fd, 
   608: 		char *destination_filename) {
   609: 
   610:   // to store the retrieved update
   611:   FILE *update_file;
   612: 
   613:   // packet structure for sending commands to the server
   614:   gzmcp_cmd newcmd;
   615: 
   616:   // for send() return values
   617:   int numbytes;
   618:   
   619:   // for reading the update file
   620:   int bytes_to_read;
   621:   unsigned char read_buf[UPD_RD_BUF_SZ];
   622: 
   623:   // for magic return value checking
   624:   unsigned char magic_found;
   625:   unsigned char last_byte;
   626:   unsigned char second_to_last_byte;
   627: 
   628:   // for returned signature
   629:   uint32_t update_signature[3];
   630: 
   631: 
   632:   // create new command/packet
   633:   newcmd.type = GZMCP_CMD_GETUPDATE;
   634:   // TODO: check for existing cached copy, and set these appropriately
   635:   newcmd.data.getupdate.size = 0;
   636:   newcmd.data.getupdate.hash = 0;
   637:   newcmd.data.getupdate.sample_data = 0;
   638:   
   639:   status("getting update as file %s...\n",
   640: 	 destination_filename);
   641:   
   642:   // send the new command
   643:   numbytes = send(cmd_socket_fd,
   644: 		  (void *)&newcmd,
   645: 		  sizeof(gzmcp_cmd), 
   646: 		  0);
   647:   // check for errors
   648:   if (numbytes < 0) {
   649:     perror("send()");
   650:     die("get-update command sending failed");
   651:   }
   652:   
   653:   // debug
   654:   status("debug: get-update sent, numbytes is %d\n",
   655: 	 numbytes);
   656:   
   657:   if (numbytes == sizeof(gzmcp_cmd)) {
   658:     status("get-update command sent\n");
   659:   } else {
   660:     die("failed to send set-parameter command");
   661:   }
   662:       
   663:   //
   664:   // receive update file from server
   665:   //
   666: 
   667:   // discard incoming data until magic is found
   668:   // todo: this could be a generic function with magic and socket_fd as arguments
   669:   status("waiting for get-update return magic...\n");
   670:   magic_found = 0;
   671:   last_byte = 0;
   672:   second_to_last_byte = 0;
   673:   while (!magic_found) {
   674:     second_to_last_byte = last_byte;
   675:     // recieve a byte
   676:     numbytes = recv(cmd_socket_fd,
   677: 		    (void *)&last_byte,
   678: 		    1, 
   679: 		    0);
   680:     // check for errors
   681:     if (numbytes < 0) {
   682:       perror("recv()");
   683:       die("get_update: waiting for return magic");
   684:     }
   685:   
   686:     if ((last_byte == 24) && 
   687: 	(second_to_last_byte == 42)) {
   688:       status("get_update: got correct return magic\n");
   689:       magic_found = 1;
   690:     } else {
   691:       status("get_update: haven't yet gotten correct return magic\n");
   692:     }
   693: 
   694:   } // end while (!magic_found)
   695: 
   696:   // read file size, signature, and sample data
   697:   numbytes = recv(cmd_socket_fd,
   698: 		  (void *)&update_signature,
   699: 		  4 * 3, 
   700: 		  0);
   701:   // check for errors
   702:   if (numbytes < 0) {
   703:     perror("recv()");
   704:     die("get_update: waiting for return signature");
   705:   } else if (numbytes != (4 * 3)) {
   706:     die("get_update: return signature incomplete %d/12 bytes received",
   707: 	numbytes);
   708:   }
   709: 
   710:   bytes_to_read = update_signature[0];
   711: 
   712:   status("get_update: return signature received: update is %d bytes\n",
   713: 	 bytes_to_read);
   714:   
   715:   // open output file
   716:   update_file = fopen(destination_filename, "w");
   717: 
   718:   if (update_file == NULL) {
   719:     status("get-update failed: cannot open update destination - %s - file for writing\n");
   720:     return;
   721:   }
   722: 
   723:   // read up to 1kb at a time until done
   724:   bytes_to_read = update_signature[0];
   725:   while (bytes_to_read) {
   726:     numbytes = recv(cmd_socket_fd,
   727: 		    (void *)read_buf,
   728: 		    UPD_RD_BUF_SZ, 
   729: 		    0);
   730:     // check for errors
   731:     if (numbytes < 0) {
   732:       perror("recv()");
   733:       die("get_update: while getting update file data");
   734:     }
   735: 
   736:     // write data to file
   737:     fwrite(read_buf,
   738: 	   1,
   739: 	   numbytes,
   740: 	   update_file);
   741: 
   742:     // update amount of work left to do
   743:     bytes_to_read -= numbytes;
   744: 
   745:   }
   746: 
   747: 
   748:   // close output file
   749:   fclose(update_file);
   750: 
   751: }
   752: 
   753: 
   754: 
   755: void connect_to_server(int portnum, struct sockaddr_in server_address) {
   756: 
   757:   // return values
   758:   int rv;
   759: 
   760: 
   761:   // create the command dispatch tcp socket
   762:   // open an internet stream socket to the server
   763:   cmd_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
   764:   if (cmd_socket_fd < 0) {
   765:     perror("socket()");
   766:     die("failed to open server socket");
   767:   }
   768: 
   769:   status("connecting to server at port %d...\n", 
   770: 	 portnum); 
   771: 
   772:   server_address.sin_port = htons(portnum);
   773: 
   774:   rv = connect(cmd_socket_fd,
   775: 	       (struct sockaddr *)&server_address,
   776: 	       sizeof(server_address));
   777:   if (rv < 0) {
   778:     perror("connect()");
   779:     die("failed to connect to server\n");
   780:   }
   781: 
   782:   // handshake with server
   783:   do_handshake(cmd_socket_fd);
   784: 
   785: } // end connect_to_server()
   786: 
   787: 
   788: 
   789: void get_and_handle_user_commands(void) {
   790: 
   791:   // for user program termination request handling
   792:   int user_exit;
   793: 
   794:   // string buffers for processing user input
   795:   char user_cmd_string[USER_CMD_MAXLEN + 1];
   796:   char user_cmd[USER_CMD_MAXLEN + 1];
   797:   char user_cmd_arga[USER_CMD_MAXLEN + 1];
   798:   char user_cmd_argb[USER_CMD_MAXLEN + 1];
   799:   char user_cmd_argc[USER_CMD_MAXLEN + 1];
   800:   // number of fields in user input
   801:   int num_items;
   802: 
   803: 
   804:   // TODO: no supported exit method exists yet
   805:   user_exit = 0;
   806: 
   807:   // handle commands given by user at stdin
   808:   while (!user_exit) {
   809: 
   810:     // prompt for a user command
   811:     status("enter a command to send to the server\n");
   812:     fprintf(stdout, "\ngzmcp command >>> ");
   813: 
   814:     // read a user command
   815:     if(fgets(user_cmd_string, USER_CMD_MAXLEN, stdin) == NULL) {
   816:       // input from terminal ceased
   817:       user_exit = 1;
   818:       status("end of user input reached, goodbye...\n");
   819:       exit(0);
   820:     }
   821: 
   822:     // output an end of line
   823:     fprintf(stdout, "\n\n");
   824: 
   825:     // extract command type and arg fields
   826:     num_items = sscanf(user_cmd_string, 
   827: 		       "%s %s %s %s",
   828: 		       user_cmd,
   829: 		       user_cmd_arga,
   830: 		       user_cmd_argb,
   831: 		       user_cmd_argc);
   832:     
   833:     // handle possible lack of command
   834:     if (num_items == 0) { // no command input
   835:       status("user input empty? try again...\n");
   836:       continue;
   837:     } 
   838:     
   839:     // check for and handle each known command
   840:     if ((strncmp(user_cmd, "exit", strlen(user_cmd)) == 0 ) ||
   841: 	(strncmp(user_cmd, "quit", strlen(user_cmd)) == 0 )) {
   842: 
   843:       if (num_items != 1) {
   844: 	status("command ignored: %s takes no arguments\n",
   845: 	       user_cmd);
   846: 	continue;
   847:       } else {
   848: 	user_exit = 1;
   849:       }
   850: 
   851:     } else if (strncmp(user_cmd, "send-handshake", strlen(user_cmd)) == 0 ) {
   852: 
   853:       if (num_items != 1) {
   854: 	status("command ignored: %s takes no arguments\n",
   855: 	       user_cmd);
   856: 	continue;
   857:       } else {
   858: 	do_handshake(cmd_socket_fd);
   859:       }
   860: 
   861:     } else if ((strncmp(user_cmd, "set-preset", strlen(user_cmd)) == 0 )) {
   862: 
   863:       if (num_items != 2) {
   864: 	status("command ignored: %s takes exactly one argument\n",
   865: 	       user_cmd);
   866: 	continue;
   867:       } else {
   868: 	set_preset(cmd_socket_fd, user_cmd_arga);
   869:       }
   870: 
   871:     } else if ((strncmp(user_cmd, "set-parameter", strlen(user_cmd)) == 0 )) {
   872: 
   873:       if (num_items != 3) {
   874: 	status("command ignored: %s takes exactly two arguments\n",
   875: 	       user_cmd);
   876: 	continue;
   877:       } else {
   878: 	set_parameter(cmd_socket_fd, user_cmd_arga, user_cmd_argb);
   879:       }
   880: 
   881:     } else if ((strncmp(user_cmd, "get-update", strlen(user_cmd)) == 0 )) {
   882: 
   883:       if (num_items != 2) {
   884: 	status("command ignored: %s takes exactly one argument\n",
   885: 	       user_cmd);
   886: 	continue;
   887:       } else {
   888: 	get_update(cmd_socket_fd, user_cmd_arga);
   889:       }
   890: 
   891:     } else {
   892: 
   893:       status("unknown command - %s - try again...\n", user_cmd);
   894: 
   895:     } // end if/else handling of known user commands
   896:     
   897:   } // end while (!user_exit)
   898: 
   899:   // later...
   900:   status("user requested exit, goodbye...\n");
   901:   exit(0);
   902: 
   903: }
   904: 
   905: 
   906: 
   907: void network_shutdown(void) {
   908: 
   909:   shutdown(cmd_socket_fd, SHUT_RDWR);
   910: 
   911: } // end network_shutdown()
   912: 
   913: 
   914: