1 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <signal.h> 6 #include <sys/ioctl.h> 7 #include <sys/stat.h> 8 #include <fcntl.h> 9 #include <unistd.h> 10 #include <string.h> 11 12 #define TRUE 1 13 #define FALSE 0 14 15 #define PLUGIN_MSG_PARSE_ERR -1 // also interpreted as 'no more output' 16 #define PLUGIN_PROGRESS_MSG 0 17 #define PLUGIN_WARN_MSG 1 18 #define PLUGIN_ERR_MSG 2 19 20 #define CHECK_INTERVAL 0.475 21 22 int add_argv (char **dest, char *content); 23 int open_a_pty (int *pty, int *tty); 24 char **create_argv_for_execution_using_shell (char *command); 25 int execute_ripper_encoder_with_plugin (char **program_argv, 26 char **plugin_argv, 27 pid_t * program_pid, pid_t * plugin_pid, 28 int *read_fd); 29 void err_handler (char *msg); 30 int read_and_process_plugin_output (int read_fd, double *progress, char *msg); 31 int parse_plugin_output (char *out, double *progress, char *msg); 32 33 int 34 add_argv (char **dest, char *content) 35 { 36 size_t i; 37 38 i = 0; 39 40 while (content[i++] != '\0'); 41 42 if ((*dest = malloc (i)) == NULL) { 43 err_handler ("Cannot allocate memory"); 44 return FALSE; 45 } 46 47 strcpy (*dest, content); 48 return TRUE; 49 } 50 51 void 52 err_handler (char *msg) 53 { 54 fprintf (stderr, "Error : %s\n", msg); 55 } 56 57 int 58 open_a_pty (int *pty, int *tty) 59 { 60 char line[12]; 61 char *p1, *p2; 62 char *cp; 63 int i; 64 65 sprintf (line, "/dev/ptyXX"); 66 p1 = &line[8]; 67 p2 = &line[9]; 68 69 for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) { 70 struct stat stb; 71 72 *p1 = *cp; 73 *p2 = '0'; 74 /* 75 * This stat() check is just to keep us from 76 * looping through all 256 combinations if there 77 * aren't that many ptys available. 78 */ 79 if (stat (line, &stb) < 0) 80 break; 81 for (i = 0; i < 16; i++) { 82 *p2 = "0123456789abcdef"[i]; 83 *pty = open (line, O_RDWR); 84 if (*pty > 0) { 85 line[5] = 't'; 86 /* Now open appropriate tty */ 87 if ((*tty = open (line, O_RDWR)) < 0) { 88 line[5] = 'p'; 89 close (*pty); 90 continue; 91 } 92 return 0; 93 } 94 } 95 } 96 return -1; 97 } 98 99 char ** 100 create_argv_for_execution_using_shell (char *command) 101 { 102 char *shell; 103 char **argv; 104 105 shell = "/bin/bash"; 106 107 if ((argv = (char **) malloc (sizeof (char *) * 4)) == NULL) { 108 err_handler ("malloc failed"); 109 return NULL; 110 } 111 argv[0] = NULL; 112 argv[1] = NULL; 113 argv[2] = NULL; 114 argv[3] = NULL; 115 116 if (add_argv (&(argv[0]), shell) == FALSE) 117 return NULL; 118 if (add_argv (&(argv[1]), "-c") == FALSE) 119 return NULL; 120 if (add_argv (&(argv[2]), command) == FALSE) 121 return NULL; 122 return argv; 123 } 124 125 int 126 execute_ripper_encoder_with_plugin (char **program_argv, 127 char **plugin_argv, 128 pid_t * program_pid, pid_t * plugin_pid, 129 int *read_fd) 130 { 131 int pty_fd0, tty_fd0, pty_fd1, tty_fd1; 132 pid_t pid; 133 134 /* Open two pty/tty pairs */ 135 if (open_a_pty (&pty_fd0, &tty_fd0) < 0) { 136 err_handler ("Cannot open pty/tty pair"); 137 return -1; 138 } 139 if (open_a_pty (&pty_fd1, &tty_fd1) < 0) { 140 close (pty_fd0); 141 close (tty_fd0); 142 err_handler ("Cannot open pty/tty pair"); 143 return -1; 144 } 145 146 // fork & exec & link plugin 147 if ((pid = fork ()) < 0) { 148 err_handler ("Cannot fork"); 149 return -1; 150 } 151 *plugin_pid = pid; 152 153 if (pid == 0) { 154 // We're in the child process 155 // save stderr 156 int stderr_fd; 157 stderr_fd = dup (2); 158 159 dup2 (pty_fd0, 0); 160 dup2 (tty_fd1, 1); 161 162 execvp (plugin_argv[0], plugin_argv); 163 164 dup2 (stderr_fd, 2); 165 perror ("Failed to exec plugin"); 166 _exit (127); 167 } 168 169 // we're in the parent process 170 close (pty_fd0); 171 close (tty_fd1); 172 *read_fd = pty_fd1; 173 174 // fork the real program 175 if ((pid = fork ()) < 0) { 176 err_handler ("Cannot fork"); 177 return -1; 178 } 179 *program_pid = pid; 180 181 if (pid == 0) { 182 // We're in the child process 183 // save stderr 184 int stderr_fd; 185 stderr_fd = dup (2); 186 187 dup2 (tty_fd0, 1); 188 dup2 (tty_fd0, 2); 189 190 execvp (program_argv[0], program_argv); 191 192 dup2 (stderr_fd, 2); 193 perror ("Failed to exec the specified program"); 194 _exit (127); 195 } 196 close (tty_fd0); 197 198 return 0; 199 } 200 201 int 202 read_and_process_plugin_output (int read_fd, double *progress, char *msg) 203 { 204 int bytes_avail; 205 char buf[1024]; 206 FILE *read_stream; 207 208 ioctl (read_fd, FIONREAD, &bytes_avail); 209 if (bytes_avail <= 0) { 210 fprintf (stderr, "*** No report available from plugin. Ctrl-C to stop\n"); 211 // the plugin hasn't printed anything yet. return PLUGIN_MSG_PARSE_ERR 212 // which the caller should ignore. 213 return PLUGIN_MSG_PARSE_ERR; 214 } 215 216 // all the lines are terminated with '\n' and if the plugin started to 217 // print something then it'll finish it soon. so using fgets is 218 // reasonable 219 220 read_stream = fdopen (read_fd, "r"); 221 if (fgets (buf, sizeof (buf), read_stream) == NULL) 222 return PLUGIN_MSG_PARSE_ERR; 223 fprintf (stderr, "*** Output read from plugin : %s", buf); 224 return parse_plugin_output (buf, progress, msg); 225 } 226 227 int 228 parse_plugin_output (char *out, double *progress, char *msg) 229 { 230 int pos, done, s, d; 231 char ch; 232 233 *progress = -1; 234 msg[0] = '\0'; 235 236 pos = 0; 237 while (out[pos] != '\0' && out[pos] != '[') 238 pos++; 239 240 // parse err point 0 : cannot find beginning '[' 241 if (out[pos] != '[') { 242 fprintf (stderr, "*** parse err at : 0 : cannot find leading '['\n"); 243 return PLUGIN_MSG_PARSE_ERR; 244 } 245 pos++; 246 247 // read the type character 248 ch = out[pos++]; 249 250 if (ch == 'P') 251 // if it's a msg reporting progess, read the percentage 252 sscanf (out + pos, "%lf", progress); 253 254 while (out[pos] != '\0' && out[pos] != '"' && out[pos] != ']') 255 pos++; 256 257 if (out[pos] == '"') { 258 // we've got some message 259 pos++; 260 261 // copy the message 262 done = 0; 263 s = pos; 264 d = 0; 265 while (!done) { 266 if (out[s] != '\\' && out[s] != '"' && 267 out[s] != ']' && out[s] != '\0') 268 msg[d++] = out[s++]; 269 else if (out[s] == '\\') { 270 msg[d] = out[s + 1]; 271 s += 2; 272 d++; 273 } else { 274 msg[d] = '\0'; 275 done = 1; 276 } 277 } 278 } 279 280 switch (ch) { 281 case 'P': 282 // parse err point 1 : invalid progress 283 if (*progress < 0 || *progress > 1) { 284 fprintf (stderr, "*** parse err at : 1 : invalid progress : %f\n", 285 *progress); 286 return PLUGIN_MSG_PARSE_ERR; 287 } 288 return PLUGIN_PROGRESS_MSG; 289 case 'W': 290 // parse err point 2 : warning report without msg 291 if (msg[0] == '\0') { 292 fprintf (stderr, "*** parse err at : 2 : warning report w/o msg\n"); 293 return PLUGIN_MSG_PARSE_ERR; 294 } 295 return PLUGIN_WARN_MSG; 296 case 'E': 297 // parse err point 3 : error report without msg 298 if (msg[0] == '\0') { 299 fprintf (stderr, "*** parse err at : 3 : error report w/o msg\n"); 300 return PLUGIN_MSG_PARSE_ERR; 301 } 302 return PLUGIN_ERR_MSG; 303 default: 304 return PLUGIN_MSG_PARSE_ERR; 305 } 306 } 307 308 int 309 main (int argc, char **argv) 310 { 311 char *pg_com, *pi_com; 312 char **pg_argv, **pi_argv; 313 pid_t pg_pid, pi_pid; 314 int read_fd; 315 double progress; 316 char msg[1024]; 317 int rs; 318 319 if (argc != 3) { 320 fprintf (stderr, "Syntax: ripperX_plugin_tester" 321 " \"program command with args\" \"plugin command\"\n"); 322 exit (1); 323 } 324 pg_com = argv[1]; 325 pi_com = argv[2]; 326 fprintf (stdout, "***Executing \"%s\"\n" 327 "with plugin \"%s\"\n", pg_com, pi_com); 328 329 pg_argv = create_argv_for_execution_using_shell (pg_com); 330 pi_argv = create_argv_for_execution_using_shell (pi_com); 331 332 pg_pid = 0; 333 pi_pid = 0; 334 335 if (execute_ripper_encoder_with_plugin (pg_argv, pi_argv, &pg_pid, &pi_pid, 336 &read_fd) < 0) 337 exit (1); 338 339 while (1) { 340 usleep (CHECK_INTERVAL * 1000000); 341 printf ("\n"); 342 if ((rs = read_and_process_plugin_output (read_fd, &progress, msg)) 343 == PLUGIN_MSG_PARSE_ERR) { 344 //if (msg != NULL) 345 //free(msg); 346 fprintf (stderr, "*** PLUGIN_MSG_PARSE_ERR returned\n"); 347 continue; 348 } 349 switch (rs) { 350 case PLUGIN_PROGRESS_MSG: 351 printf ("Report type : progress, Progress : %f,\nMsg : %s\n", 352 progress, msg); 353 break; 354 case PLUGIN_WARN_MSG: 355 printf ("Report type : warning,\nMsg : %s\n", msg); 356 break; 357 case PLUGIN_ERR_MSG: 358 printf ("Report type : error,\nMsg : %s\n", msg); 359 break; 360 } 361 } 362 363 return 0; 364 } 365