int main (int argc, char *argv[]) { /*************/ /* image I/O */ /*************/ CONC_BLOCK conc_block; /* conc i/o control block */ FILE *imgfp, *ROIfp, *outfp, *keyfp; char imgroot[MAXL], imgfile[MAXL], ROIroot[MAXL], ROIfile[MAXL], outfile[MAXL] = "", keyfile[MAXL], tmpfile[MAXL]; /********************/ /* iamge processing */ /********************/ int imgdim[4], ROIdim[4], diedim[3], vdim, imgori, ROIori, isbig, isbigm; int lendie = LENDIE, ncrit = NCRIT, ndie, mdie, *mdielist; int ix, iy, iz, jx, jy, jz; float imgvox[3], ROIvox[3]; float *imgt, *imgr; DIEDAT *diedat; /*************************/ /* timeseries processing */ /*************************/ char *format; double tol = TOL, **b, *lambda, **v; int nnez, nred; /***********/ /* utility */ /***********/ char *ptr, command[2*MAXL]; double q; int c, i, j, k, l, m, n; /*********/ /* flags */ /*********/ int outtype = 0; int conc_flag = 0; int status = 0; int count_zero = 0; int defined; /* test on imgt and imgr voxels */ int test_svd = 1; int H_flag = 0; int K_flag = 0; int V_flag = 0; /**************/ /* debug code */ /**************/ double **bcopy, **e, errmax; setprog (program, argv); if (!(format = (char *) calloc (1024, sizeof (char)))) errm (program); /************************/ /* process command line */ /************************/ for (j = 0, i = 1; i < argc; i++) { if (*argv[i] == '-') { strcpy (command, argv[i]); ptr = command; while (c = *ptr++) switch (c) { case 'Z': count_zero++; break; case 'V': V_flag++; break; case 'H': H_flag++; break; case 'K': K_flag++; break; case 'O': outtype = atoi (ptr); *ptr = '\0'; break; case 'l': lendie = atoi (ptr); *ptr = '\0'; break; case 'f': strncpy (format, ptr, 1023); *ptr = '\0'; break; case 'n': ncrit = atoi (ptr); *ptr = '\0'; break; case 't': tol = atof (ptr); *ptr = '\0'; break; case 'o': strcpy (outfile, ptr); *ptr = '\0'; break; default: break; } } else switch (j) { case 0: getroot (argv[i], imgroot); conc_flag = (strstr (argv[i], ".conc") == argv[i] + strlen (imgroot)); j++; break; case 1: getroot (argv[i], ROIroot); j++; break; default: break; } } if (j < 2) usage (program); if (lendie <= 0 || ncrit <= 0) usage (program); if (strlen (outfile)) { if (!(outfp = fopen (outfile, "w"))) errw (program, outfile); printf ("Writing: %s\n", outfile); } else { outfp = stdout; } if (H_flag) write_command_line (outfp, argc, argv); /**************************************/ /* get input 4dfp dims and open files */ /**************************************/ if (conc_flag) { conc_init_quiet (&conc_block, program); conc_open_quiet (&conc_block, imgroot); strcpy (imgfile, conc_block.lstfile); for (k = 0; k < 4; k++) imgdim[k] = conc_block.imgdim[k]; for (k = 0; k < 3; k++) imgvox[k] = conc_block.voxdim[k]; imgori = conc_block.orient; isbig = conc_block.isbig; } else { sprintf (imgfile, "%s.4dfp.img", imgroot); if (get_4dfp_dimoe_quiet (imgfile, imgdim, imgvox, &imgori, &isbig) < 0) errr (program, imgfile); if (!(imgfp = fopen (imgfile, "rb"))) errr (program, imgfile); } /*****************/ /* alloc buffers */ /*****************/ vdim = imgdim[0]*imgdim[1]*imgdim[2]; if (!(imgt = (float *) malloc (vdim*sizeof (float)))) errm (program); if (!(imgr = (float *) malloc (vdim*sizeof (float)))) errm (program); /****************************/ /* prepare dice from ROIimg */ /****************************/ sprintf (ROIfile, "%s.4dfp.img", ROIroot); if (get_4dfp_dimoe_quiet (ROIfile, ROIdim, ROIvox, &ROIori, &isbigm) < 0) errr (program, ROIfile); status = ROIori - imgori; for (k = 0; k < 3; k++) status |= (imgdim[k] != ROIdim[k]); for (k = 0; k < 3; k++) status |= (fabs (imgvox[k] - ROIvox[k]) > 0.0001); if (status) { fprintf (stderr, "%s: %s %s vdim mismatch\n", program, imgroot, ROIroot); exit (-1); } fprintf (stdout, "Reading: %s\n", ROIfile); if (!(ROIfp = fopen (ROIfile, "rb")) || eread (imgr, vdim, isbigm, ROIfp) || fclose (ROIfp)) errr (program, ROIfile); ndie = 1; for (k = 0; k < 3; k++) { diedim[k] = imgdim[k]/lendie; ndie *= diedim[k]; } if (0) printf ("diedim %d %d %d ndie %d\n", diedim[0], diedim[1], diedim[2], ndie); if (!(diedat = (DIEDAT *) calloc (ndie, sizeof (DIEDAT)))) errm (program); j = 0; /* index of die */ for (iz = 0; iz < imgdim[2] - (imgdim[2] % lendie); iz += lendie) { for (iy = 0; iy < imgdim[1] - (imgdim[1] % lendie); iy += lendie) { for (ix = 0; ix < imgdim[0] - (imgdim[0] % lendie); ix += lendie) { if (0) printf ("ix, iy, iz, j %5d%5d%5d%10d\n", ix, iy, iz, j); diedat[j].fx = ix + lendie/2. - 0.5; diedat[j].fy = iy + lendie/2. - 0.5; diedat[j].fz = iz + lendie/2. - 0.5; j++; }}} assert (j == ndie); for (iz = 0; iz < imgdim[2] - (imgdim[2] % lendie); iz++) {jz = iz/lendie; for (iy = 0; iy < imgdim[1] - (imgdim[1] % lendie); iy++) {jy = iy/lendie; for (ix = 0; ix < imgdim[0] - (imgdim[0] % lendie); ix++) {jx = ix/lendie; j = jx + diedim[0]*(jy + diedim[1]*jz); /* index of die */ i = ix + imgdim[0]*(iy + imgdim[1]*iz); /* index of voxel */ defined = isnormal (imgr[i]) && imgr[i] != (float) 1.e-37; if (defined) { if (diedat[j].n == diedat[j].ma) { diedat[j].ma += INCR; if (!diedat[j].p) { diedat[j].p = malloc (diedat[j].ma*sizeof (int)); } else { diedat[j].p = realloc (diedat[j].p, diedat[j].ma*sizeof (int)); } if (!diedat[j].p) errm (program); } diedat[j].p[diedat[j].n++] = i; } i++; }}} /********************/ /* create mdie list */ /********************/ for (mdie = j = 0; j < ndie; j++) { if (V_flag) printf ("j diedat[j].n %5d %5d\n", j, diedat[j].n); if (diedat[j].n >= ncrit) mdie++; } printf ("defined die count = %d\n", mdie); if (!(mdielist = (int *) calloc (mdie, sizeof (int)))) errm (program); for (mdie = j = 0; j < ndie; j++) if (diedat[j].n >= ncrit) mdielist[mdie++] = j; if (K_flag) { /*********************************/ /* create voxel address key file */ /*********************************/ sprintf (tmpfile, "temp%d", getpid ()); if (!(keyfp = fopen (tmpfile, "w"))) errw (program, tmpfile); for (m = 0; m < mdie; m++) { j = mdielist[m]; /* index of die with at least ncrit voxels in ROI mask */ fprintf (keyfp, "%10.2f%10.2f%10.2f\n", diedat[j].fx, diedat[j].fy, diedat[j].fz); } if (fclose (keyfp)) errw (program, tmpfile); strcpy (keyfile, imgroot); if (!(ptr = strrchr (keyfile, '/'))) ptr = keyfile; else ptr++; strcpy (keyfile, ptr); strcat (keyfile, "_"); strcpy (command, ROIroot); if (!(ptr = strrchr (command, '/'))) ptr = command; else ptr++; strcat (keyfile, ptr); strcat (keyfile, "_indices.txt"); if (!(keyfp = fopen (keyfile, "w"))) errw (program, keyfile); printf ("Writing: %s\n", keyfile); write_command_line (keyfp, argc, argv); if (fclose (keyfp)) errw (program, keyfile); sprintf (command, "index2atl %s %s >> %s", (conc_flag) ? conc_block.imgfile0[0] : imgroot, tmpfile, keyfile); printf ("%s\n", command); status |= system (command); remove (tmpfile); if (status) exit (-1); } /****************/ /* parse format */ /****************/ if (!(format = (char *) realloc (format, (imgdim[3] + 1)*sizeof (char)))) errm (program); if (strlen (format)) { if (k = expandf (format, imgdim[3] + 1)) exit (-1); if (strlen (format) != imgdim[3]) { fprintf (stderr, "%s: %s frame-to-count format length mismatch\n", program, imgroot); exit (-1); } for (nnez = k = 0; k < imgdim[3]; k++) if (format[k] != 'x') nnez++; } else { nnez = imgdim[3]; for (i = 0; i < imgdim[3]; i++) format[i] = '+'; } printf ("%s\n", format); printf ("%s: time series defined for %d frames, %d exluded\n", program, imgdim[3], imgdim[3] - nnez); /***************************/ /* allocate memory for svd */ /***************************/ if (!(lambda = (double *) calloc (nnez, sizeof (double)))) errm (program); v = calloc_double2 (nnez, nnez); b = calloc_double2 (mdie, nnez); /***********/ /* process */ /***********/ printf ("Reading: %s\n", imgfile); for (n = k = 0; k < imgdim[3]; k++) { if (conc_flag) { conc_read_vol (&conc_block, imgt); } else { if (eread (imgt, vdim, isbig, imgfp)) errr (program, imgfile); } for (m = 0; m < mdie; m++) { j = mdielist[m]; /* index of die with at least ncrit voxels in ROI mask */ diedat[j].v = diedat[j].m = 0; /* initialize anew for each frame */ for (l = 0; l < diedat[j].n; l++) { /* loop on voxels */ i = diedat[j].p[l]; /* index into imgt */ defined = isnormal (imgt[i]) && imgt[i] != (float) 1.e-37; if (count_zero && imgt[i] == 0.) defined = 1; if (defined) { diedat[j].v += imgt[i]; diedat[j].m++; } } if (diedat[j].m < ncrit) { fprintf (stderr, "%s: %s contains undefined voxels within %s mask\n", program, imgfile, ROIroot); exit (-1); } diedat[j].v /= diedat[j].m; } if (format[k] != 'x') { for (m = 0; m < mdie; m++) { j = mdielist[m]; /* index of die with at least ncrit voxels in ROI mask */ b[m][n] = diedat[j].v; } n++; /* increment non-skipped frames count */ } } assert (n == nnez); free (imgt); free (imgr); if (conc_flag) { conc_free (&conc_block); } else { if (fclose (imgfp)) errr (program, imgfile); } if (!outtype) goto DONE; /*****************************************************/ /* make timeseries zero mean (but not unit variance) */ /*****************************************************/ if (outtype > 1) for (j = 0; j < mdie; j++) dzeromean (b[j], nnez); /*******************************/ /* output extracted timeseries */ /*******************************/ if (outtype == 1 || outtype == 2) { for (n = k = 0; k < imgdim[3]; k++) { for (m = 0; m < mdie; m++) fprintf (outfp, " %9.4f", (format[k] != 'x') ? b[m][n] : 0.0); if (format[k] != 'x') n++; if (mdie) fprintf (outfp, "\n"); } assert (n == nnez); goto DONE; } if (test_svd) { bcopy = calloc_double2 (mdie, nnez); e = calloc_double2 (mdie, nnez); for (j = 0; j < mdie; j++) for (i = 0; i < nnez; i++) bcopy[j][i] = b[j][i]; } dsvdcmp0 (b, mdie, nnez, lambda, v); nred = ndsvdcmp0 (b, mdie, nnez, lambda, v, tol); if (H_flag) { fprintf (outfp, "#reduced dimensionality = %d\n", nred); fprintf (outfp, "#lambda after ndsvdcmp0"); for (i = 0; i < nred; i++) fprintf (outfp, " %.2f", lambda[i]); fprintf (outfp, "\n"); } if (test_svd) { for (j = 0; j < mdie; j++) for (i = 0; i < nnez; i++) for (k = 0; k < nred; k++) { e[j][i] += v[i][k]*lambda[k]*b[j][k]; } for (errmax = i = 0; i < nnez; i++) for (j = 0; j < mdie; j++) { q = fabs (e[j][i] - bcopy[j][i]); if (q > errmax) errmax = q; } if (H_flag) fprintf (outfp, "#maximum error = %.3g\n", errmax); free_double2 (bcopy); free_double2 (e); } for (k = 0; k < nnez; k++) for (j = 0; j < nred; j++) { v[k][j] *= (outtype == 3) ? lambda[j] : sqrt ((double) nnez); } /******************************/ /* output computed timeseries */ /******************************/ if (outtype == 3 || outtype == 4) { for (n = k = 0; k < imgdim[3]; k++) { for (m = 0; m < nred; m++) fprintf (outfp, " %9.4f", (format[k] != 'x') ? v[n][m] : 0.0); if (format[k] != 'x') n++; if (mdie) fprintf (outfp, "\n"); } assert (n == nnez); } /*********************/ /* clean up and exit */ /*********************/ DONE: if (strlen (outfile)) if (fclose (outfp)) errw (program, outfile); free_double2 (v); free_double2 (b); free (lambda); free (mdielist); for (j = 0; j < ndie; j++) if (diedat[j].p) free (diedat[j].p); free (diedat); free (format); exit (status); }
int main(int argc, char *argv[]) { int i; BOOL verbose = TRUE; BOOL very_verbose = TRUE; F64 start_time = 0.0; F64 full_start_time = 0.0; const CHAR* xml_output_file = 0; BOOL one_report_per_file = FALSE; U32 num_pass = 0; U32 num_fail = 0; U32 num_warning = 0; fprintf(stderr, "This is version '%s' of the LAS validator. Please contact\n", "GRiD-1"); fprintf(stderr, "me at '*****@*****.**' if you disagree with\n"); fprintf(stderr, "validation reports, want additional checks, or find bugs as\n"); fprintf(stderr, "the software is still under development. Your feedback will\n"); fprintf(stderr, "help to finish it sooner.\n"); LASreadOpener lasreadopener; if (argc == 1) { fprintf(stderr,"lasvalidate.exe is best run with arguments in the command line\n"); char file_name[256]; fprintf(stderr,"enter input LAS file name: "); fgets(file_name, 256, stdin); file_name[strlen(file_name)-1] = '\0'; lasreadopener.set_file_name(file_name); fprintf(stderr,"enter output XML file name: "); fgets(file_name, 256, stdin); file_name[strlen(file_name)-1] = '\0'; xml_output_file = strdup(file_name); } for (i = 1; i < argc; i++) { if (strcmp(argv[i],"-version") == 0) { fprintf(stderr, "\nlasvalidate %d with LASread (v %d.%d %d) and LAScheck (v %d.%d %d) by rapidlasso GmbH\n", VALIDATE_VERSION, LASREAD_VERSION_MAJOR, LASREAD_VERSION_MINOR, LASREAD_BUILD_DATE, LASCHECK_VERSION_MAJOR, LASCHECK_VERSION_MINOR, LASCHECK_BUILD_DATE); byebye(LAS_VALIDATE_SUCCESS); } else if (strcmp(argv[i],"-h") == 0 || strcmp(argv[i],"-help") == 0) { lasreadopener.usage(); usage(LAS_VALIDATE_SUCCESS); } else if (strcmp(argv[i],"-v") == 0 || strcmp(argv[i],"-verbose") == 0) { verbose = TRUE; } else if (strcmp(argv[i],"-vv") == 0 || strcmp(argv[i],"-very_verbose") == 0) { verbose = TRUE; very_verbose = TRUE; } else if (strcmp(argv[i],"-i") == 0) { if ((i+1) >= argc) { fprintf(stderr,"ERROR: '%s' needs at least 1 argument: file_name or wild_card\n", argv[i]); usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX); } i+=1; do { lasreadopener.add_file_name(argv[i]); i+=1; } while (i < argc && *argv[i] != '-'); i-=1; } else if (strcmp(argv[i],"-irec") == 0) { if ((i+1) >= argc) { fprintf(stderr,"ERROR: '%s' needs at least 1 argument: directory_name\n", argv[i]); usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX); } i+=1; do { lasreadopener.add_directory(argv[i], TRUE); i+=1; } while (i < argc && *argv[i] != '-'); i-=1; } else if (strcmp(argv[i],"-stdin") == 0) { lasreadopener.set_piped(TRUE); } else if (strcmp(argv[i],"-lof") == 0) { if ((i+1) >= argc) { fprintf(stderr,"ERROR: '%s' needs 1 argument: list_of_files\n", argv[i]); usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX); } FILE* file = fopen(argv[i+1], "r"); if (file == 0) { fprintf(stderr, "ERROR: cannot open '%s'\n", argv[i+1]); return FALSE; } char line[1024]; while (fgets(line, 1024, file)) { // find end of line int len = strlen(line) - 1; // remove extra white spaces and line return at the end while (len > 0 && ((line[len] == '\n') || (line[len] == ' ') || (line[len] == '\t') || (line[len] == '\012'))) { line[len] = '\0'; len--; } lasreadopener.add_file_name(line); } fclose(file); i+=1; } else if (strcmp(argv[i],"-o") == 0) { i++; xml_output_file = argv[i]; } else if (strcmp(argv[i],"-oxml") == 0) { one_report_per_file = TRUE; } else { fprintf(stderr, "ERROR: cannot understand argument '%s'\n", argv[i]); usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX); } } // in verbose mode we measure the total time if (verbose) full_start_time = taketime(); // check input if (!lasreadopener.is_active()) { fprintf(stderr,"ERROR: no input specified\n"); byebye(LAS_VALIDATE_NO_INPUT_SPECIFIED, argc == 1); } // output logging XMLwriter xmlwriter; if (lasreadopener.is_active()) { if (xml_output_file) { one_report_per_file = FALSE; } else if (!one_report_per_file) { xml_output_file = "validate.xml"; } } // maybe we are doing one summary report if (xml_output_file) { if (!xmlwriter.open(xml_output_file, "LASvalidator")) { byebye(LAS_VALIDATE_WRITE_PERMISSION_ERROR, argc == 1); } } // accumulated pass U32 total_pass = VALIDATE_PASS; // possibly loop over multiple input files while (lasreadopener.is_active()) { // in very verbose mode we measure the time for each file if (very_verbose) start_time = taketime(); // open lasreader LASreader* lasreader = lasreadopener.open(); if (lasreader == 0) { fprintf(stderr, "ERROR: could not open lasreader\n"); byebye(LAS_VALIDATE_INPUT_FILE_NOT_FOUND, argc == 1); } // get a pointer to the header LASheader* lasheader = &lasreader->header; // maybe we are doing one report per file if (one_report_per_file) { int len = strlen(lasreadopener.get_path()); CHAR* current_xml_output_file = (CHAR*)malloc(len + 5); strcpy(current_xml_output_file, lasreadopener.get_path()); current_xml_output_file[len-4] = '_'; current_xml_output_file[len-3] = 'L'; current_xml_output_file[len-2] = 'V'; current_xml_output_file[len-1] = 'S'; current_xml_output_file[len ] = '.'; current_xml_output_file[len+1] = 'x'; current_xml_output_file[len+2] = 'm'; current_xml_output_file[len+3] = 'l'; current_xml_output_file[len+4] = '\0'; if (!xmlwriter.open(current_xml_output_file, "LASvalidator")) { byebye(LAS_VALIDATE_WRITE_PERMISSION_ERROR, argc == 1); } free(current_xml_output_file); } // start a new report xmlwriter.begin("report"); // report description of file xmlwriter.beginsub("file"); xmlwriter.write("name", lasreadopener.get_file_name()); xmlwriter.write("path", lasreadopener.get_path()); CHAR temp[32]; sprintf(temp, "%d.%d", lasheader->version_major, lasheader->version_minor); xmlwriter.write("version", temp); strncpy(temp, lasheader->system_identifier, 32); temp[31] = '\0'; xmlwriter.write("system_identifier", temp); strncpy(temp, lasheader->generating_software, 32); temp[31] = '\0'; xmlwriter.write("generating_software", temp); xmlwriter.write("point_data_format", lasheader->point_data_format); CHAR crsdescription[512]; strcpy(crsdescription, "not valid or not specified"); if (lasheader->fails == 0) { // header was loaded. now parse and check. LAScheck lascheck(lasheader); while (lasreader->read_point()) { lascheck.parse(&lasreader->point); } // check header and points and get CRS description lascheck.check(lasheader, crsdescription); } xmlwriter.write("CRS", crsdescription); xmlwriter.endsub("file"); // report the verdict U32 pass = (lasheader->fails ? VALIDATE_FAIL : VALIDATE_PASS); if (lasheader->warnings) pass |= VALIDATE_WARNING; xmlwriter.beginsub("summary"); xmlwriter.write((pass == VALIDATE_PASS ? "pass" : ((pass & VALIDATE_FAIL) ? "fail" : "warning"))); xmlwriter.endsub("summary"); // report details (if necessary) if (pass != VALIDATE_PASS) { xmlwriter.beginsub("details"); for (i = 0; i < lasheader->fail_num; i+=2) { xmlwriter.write(lasheader->fails[i], "fail", lasheader->fails[i+1]); } for (i = 0; i < lasheader->warning_num; i+=2) { xmlwriter.write(lasheader->warnings[i], "warning", lasheader->warnings[i+1]); } xmlwriter.endsub("details"); total_pass |= pass; if (pass & VALIDATE_FAIL) { num_fail++; } else { num_warning++; } } else { num_pass++; } // end the report xmlwriter.end("report"); // maybe we are doing one report per file if (one_report_per_file) { // report the total verdict xmlwriter.begin("total"); xmlwriter.write((total_pass == VALIDATE_PASS ? "pass" : ((total_pass & VALIDATE_FAIL) ? "fail" : "warning"))); xmlwriter.beginsub("details"); xmlwriter.write("pass", num_pass); xmlwriter.write("warning", num_warning); xmlwriter.write("fail", num_fail); xmlwriter.endsub("details"); xmlwriter.end("total"); num_pass = 0; num_warning = 0; num_fail = 0; // write which validator was used write_version(xmlwriter); // write which command line was used write_command_line(xmlwriter, argc, argv); // close the LASvalidator XML output file xmlwriter.close("LASvalidator"); } lasreader->close(); delete lasreader; // in very verbose mode we report the time for each file if (very_verbose) { fprintf(stderr,"needed %.2f sec for '%s'\n", taketime()-start_time, lasreadopener.get_file_name()); start_time = taketime(); } } // maybe we are doing one summary report if (!one_report_per_file) { // report the total verdict xmlwriter.begin("total"); xmlwriter.write((total_pass == 0 ? "pass" : ((total_pass & 1) ? "fail" : "warning"))); xmlwriter.beginsub("details"); xmlwriter.write("pass", num_pass); xmlwriter.write("warning", num_warning); xmlwriter.write("fail", num_fail); xmlwriter.endsub("details"); xmlwriter.end("total"); // write which validator was used write_version(xmlwriter); // write which command line was used write_command_line(xmlwriter, argc, argv); // close the LASvalidator XML output file xmlwriter.close("LASvalidator"); } // in verbose mode we report the total time if (verbose && (lasreadopener.get_file_name_number() > 1)) { fprintf(stderr,"done. total time %.2f sec.\n", taketime()-full_start_time); } byebye(argc==1); return 0; }
/* process HTTP or SSDP requests */ int main(int argc, char * * argv) { int i; int shttpl = -1; /* socket for HTTP */ int sudp = -1; /* IP v4 socket for receiving SSDP */ #ifdef ENABLE_IPV6 int sudpv6 = -1; /* IP v6 socket for receiving SSDP */ #endif #ifdef ENABLE_NATPMP int * snatpmp = NULL; #endif #ifdef ENABLE_NFQUEUE int nfqh = -1; #endif #ifdef USE_IFACEWATCHER int sifacewatcher = -1; #endif int * snotify = NULL; int addr_count; LIST_HEAD(httplisthead, upnphttp) upnphttphead; struct upnphttp * e = 0; struct upnphttp * next; fd_set readset; /* for select() */ fd_set writeset; struct timeval timeout, timeofday, lasttimeofday = {0, 0}; int max_fd = -1; #ifdef USE_MINIUPNPDCTL int sctl = -1; LIST_HEAD(ctlstructhead, ctlelem) ctllisthead; struct ctlelem * ectl; struct ctlelem * ectlnext; #endif struct runtime_vars v; /* variables used for the unused-rule cleanup process */ struct rule_state * rule_list = 0; struct timeval checktime = {0, 0}; struct lan_addr_s * lan_addr; #ifdef ENABLE_6FC_SERVICE unsigned int next_pinhole_ts; #endif if(init(argc, argv, &v) != 0) return 1; /* count lan addrs */ addr_count = 0; for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) addr_count++; if(addr_count > 0) { #ifndef ENABLE_IPV6 snotify = calloc(addr_count, sizeof(int)); #else /* one for IPv4, one for IPv6 */ snotify = calloc(addr_count * 2, sizeof(int)); #endif } #ifdef ENABLE_NATPMP if(addr_count > 0) { snatpmp = malloc(addr_count * sizeof(int)); for(i = 0; i < addr_count; i++) snatpmp[i] = -1; } #endif LIST_INIT(&upnphttphead); #ifdef USE_MINIUPNPDCTL LIST_INIT(&ctllisthead); #endif if( #ifdef ENABLE_NATPMP !GETFLAG(ENABLENATPMPMASK) && #endif !GETFLAG(ENABLEUPNPMASK) ) { syslog(LOG_ERR, "Why did you run me anyway?"); return 0; } syslog(LOG_INFO, "Starting%s%swith external interface %s", #ifdef ENABLE_NATPMP GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP " : " ", #else " ", #endif GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "", ext_if_name); if(GETFLAG(ENABLEUPNPMASK)) { /* open socket for HTTP connections. Listen on the 1st LAN address */ shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0); if(shttpl < 0) { syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING"); return 1; } if(v.port <= 0) { struct sockaddr_in sockinfo; socklen_t len = sizeof(struct sockaddr_in); if (getsockname(shttpl, (struct sockaddr *)&sockinfo, &len) < 0) { syslog(LOG_ERR, "getsockname(): %m"); return 1; } v.port = ntohs(sockinfo.sin_port); } syslog(LOG_NOTICE, "HTTP listening on port %d", v.port); #ifdef ENABLE_IPV6 if(find_ipv6_addr(NULL, ipv6_addr_for_http_with_brackets, sizeof(ipv6_addr_for_http_with_brackets)) > 0) { syslog(LOG_NOTICE, "HTTP IPv6 address given to control points : %s", ipv6_addr_for_http_with_brackets); } else { memcpy(ipv6_addr_for_http_with_brackets, "[::1]", 6); syslog(LOG_WARNING, "no HTTP IPv6 address"); } #endif /* open socket for SSDP connections */ sudp = OpenAndConfSSDPReceiveSocket(0); if(sudp < 0) { syslog(LOG_NOTICE, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd"); if(SubmitServicesToMiniSSDPD(lan_addrs.lh_first->str, v.port) < 0) { syslog(LOG_ERR, "Failed to connect to MiniSSDPd. EXITING"); return 1; } } #ifdef ENABLE_IPV6 sudpv6 = OpenAndConfSSDPReceiveSocket(1); if(sudpv6 < 0) { syslog(LOG_WARNING, "Failed to open socket for receiving SSDP (IP v6)."); } #endif /* open socket for sending notifications */ if(OpenAndConfSSDPNotifySockets(snotify) < 0) { syslog(LOG_ERR, "Failed to open sockets for sending SSDP notify " "messages. EXITING"); return 1; } #ifdef USE_IFACEWATCHER /* open socket for kernel notifications about new network interfaces */ if (sudp >= 0) { sifacewatcher = OpenAndConfInterfaceWatchSocket(); if (sifacewatcher < 0) { syslog(LOG_ERR, "Failed to open socket for receiving network interface notifications"); } } #endif } #ifdef ENABLE_NATPMP /* open socket for NAT PMP traffic */ if(GETFLAG(ENABLENATPMPMASK)) { if(OpenAndConfNATPMPSockets(snatpmp) < 0) { syslog(LOG_ERR, "Failed to open sockets for NAT PMP."); } else { syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u", NATPMP_PORT); } #if 0 ScanNATPMPforExpiration(); #endif } #endif /* for miniupnpdctl */ #ifdef USE_MINIUPNPDCTL sctl = OpenAndConfCtlUnixSocket("/var/run/miniupnpd.ctl"); #endif #ifdef ENABLE_NFQUEUE if ( nfqueue != -1 && n_nfqix > 0) { nfqh = OpenAndConfNFqueue(); if(nfqh < 0) { syslog(LOG_ERR, "Failed to open fd for NFQUEUE."); return 1; } else { syslog(LOG_NOTICE, "Opened NFQUEUE %d",nfqueue); } } #endif /* main loop */ while(!quitting) { /* Correct startup_time if it was set with a RTC close to 0 */ if((startup_time<60*60*24) && (time(NULL)>60*60*24)) { set_startup_time(GETFLAG(SYSUPTIMEMASK)); } /* send public address change notifications if needed */ if(should_send_public_address_change_notif) { syslog(LOG_DEBUG, "should send external iface address change notification(s)"); #ifdef ENABLE_NATPMP if(GETFLAG(ENABLENATPMPMASK)) SendNATPMPPublicAddressChangeNotification(snatpmp, addr_count); #endif #ifdef ENABLE_EVENTS if(GETFLAG(ENABLEUPNPMASK)) { upnp_event_var_change_notify(EWanIPC); } #endif should_send_public_address_change_notif = 0; } /* Check if we need to send SSDP NOTIFY messages and do it if * needed */ if(gettimeofday(&timeofday, 0) < 0) { syslog(LOG_ERR, "gettimeofday(): %m"); timeout.tv_sec = v.notify_interval; timeout.tv_usec = 0; } else { /* the comparaison is not very precise but who cares ? */ if(timeofday.tv_sec >= (lasttimeofday.tv_sec + v.notify_interval)) { if (GETFLAG(ENABLEUPNPMASK)) SendSSDPNotifies2(snotify, (unsigned short)v.port, v.notify_interval << 1); memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval)); timeout.tv_sec = v.notify_interval; timeout.tv_usec = 0; } else { timeout.tv_sec = lasttimeofday.tv_sec + v.notify_interval - timeofday.tv_sec; if(timeofday.tv_usec > lasttimeofday.tv_usec) { timeout.tv_usec = 1000000 + lasttimeofday.tv_usec - timeofday.tv_usec; timeout.tv_sec--; } else { timeout.tv_usec = lasttimeofday.tv_usec - timeofday.tv_usec; } } } /* remove unused rules */ if( v.clean_ruleset_interval && (timeofday.tv_sec >= checktime.tv_sec + v.clean_ruleset_interval)) { if(rule_list) { remove_unused_rules(rule_list); rule_list = NULL; } else { rule_list = get_upnp_rules_state_list(v.clean_ruleset_threshold); } memcpy(&checktime, &timeofday, sizeof(struct timeval)); } /* Remove expired port mappings, based on UPnP IGD LeaseDuration * or NAT-PMP lifetime) */ if(nextruletoclean_timestamp && ((unsigned int)timeofday.tv_sec >= nextruletoclean_timestamp)) { syslog(LOG_DEBUG, "cleaning expired Port Mappings"); get_upnp_rules_state_list(0); } if(nextruletoclean_timestamp && ((unsigned int)timeout.tv_sec >= (nextruletoclean_timestamp - timeofday.tv_sec))) { timeout.tv_sec = nextruletoclean_timestamp - timeofday.tv_sec; timeout.tv_usec = 0; syslog(LOG_DEBUG, "setting timeout to %u sec", (unsigned)timeout.tv_sec); } #ifdef ENABLE_NATPMP #if 0 /* Remove expired NAT-PMP mappings */ while(nextnatpmptoclean_timestamp && (timeofday.tv_sec >= nextnatpmptoclean_timestamp + startup_time)) { /*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/ if(CleanExpiredNATPMP() < 0) { syslog(LOG_ERR, "CleanExpiredNATPMP() failed"); break; } } if(nextnatpmptoclean_timestamp && timeout.tv_sec >= (nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec)) { /*syslog(LOG_DEBUG, "setting timeout to %d sec", nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/ timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec; timeout.tv_usec = 0; } #endif #endif #ifdef ENABLE_6FC_SERVICE /* Clean up expired IPv6 PinHoles */ next_pinhole_ts = 0; upnp_clean_expired_pinholes(&next_pinhole_ts); if(next_pinhole_ts && timeout.tv_sec >= (int)(next_pinhole_ts - timeofday.tv_sec)) { timeout.tv_sec = next_pinhole_ts - timeofday.tv_sec; timeout.tv_usec = 0; } #endif /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); FD_ZERO(&writeset); if (sudp >= 0) { FD_SET(sudp, &readset); max_fd = MAX( max_fd, sudp); #ifdef USE_IFACEWATCHER if (sifacewatcher >= 0) { FD_SET(sifacewatcher, &readset); max_fd = MAX(max_fd, sifacewatcher); } #endif } if (shttpl >= 0) { FD_SET(shttpl, &readset); max_fd = MAX( max_fd, shttpl); } #ifdef ENABLE_IPV6 if (sudpv6 >= 0) { FD_SET(sudpv6, &readset); max_fd = MAX( max_fd, sudpv6); } #endif #ifdef ENABLE_NFQUEUE if (nfqh >= 0) { FD_SET(nfqh, &readset); max_fd = MAX( max_fd, nfqh); } #endif i = 0; /* active HTTP connections count */ for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if(e->socket >= 0) { if(e->state <= EWaitingForHttpContent) FD_SET(e->socket, &readset); else if(e->state == ESendingAndClosing) FD_SET(e->socket, &writeset); else continue; max_fd = MAX(max_fd, e->socket); i++; } } /* for debug */ #ifdef DEBUG if(i > 1) { syslog(LOG_DEBUG, "%d active incoming HTTP connections", i); } #endif #ifdef ENABLE_NATPMP for(i=0; i<addr_count; i++) { if(snatpmp[i] >= 0) { FD_SET(snatpmp[i], &readset); max_fd = MAX( max_fd, snatpmp[i]); } } #endif #ifdef USE_MINIUPNPDCTL if(sctl >= 0) { FD_SET(sctl, &readset); max_fd = MAX( max_fd, sctl); } for(ectl = ctllisthead.lh_first; ectl; ectl = ectl->entries.le_next) { if(ectl->socket >= 0) { FD_SET(ectl->socket, &readset); max_fd = MAX( max_fd, ectl->socket); } } #endif #ifdef ENABLE_EVENTS upnpevents_selectfds(&readset, &writeset, &max_fd); #endif if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) { if(quitting) goto shutdown; if(errno == EINTR) continue; /* interrupted by a signal, start again */ syslog(LOG_ERR, "select(all): %m"); syslog(LOG_ERR, "Failed to select open sockets. EXITING"); return 1; /* very serious cause of error */ } #ifdef USE_MINIUPNPDCTL for(ectl = ctllisthead.lh_first; ectl;) { ectlnext = ectl->entries.le_next; if((ectl->socket >= 0) && FD_ISSET(ectl->socket, &readset)) { char buf[256]; int l; l = read(ectl->socket, buf, sizeof(buf)); if(l > 0) { /*write(ectl->socket, buf, l);*/ write_command_line(ectl->socket, argc, argv); write_option_list(ectl->socket); write_permlist(ectl->socket, upnppermlist, num_upnpperm); write_upnphttp_details(ectl->socket, upnphttphead.lh_first); write_ctlsockets_list(ectl->socket, ctllisthead.lh_first); write_ruleset_details(ectl->socket); #ifdef ENABLE_EVENTS write_events_details(ectl->socket); #endif /* close the socket */ close(ectl->socket); ectl->socket = -1; } else { close(ectl->socket); ectl->socket = -1; } } if(ectl->socket < 0) { LIST_REMOVE(ectl, entries); free(ectl); } ectl = ectlnext; } if((sctl >= 0) && FD_ISSET(sctl, &readset)) { int s; struct sockaddr_un clientname; struct ctlelem * tmp; socklen_t clientnamelen = sizeof(struct sockaddr_un); //syslog(LOG_DEBUG, "sctl!"); s = accept(sctl, (struct sockaddr *)&clientname, &clientnamelen); syslog(LOG_DEBUG, "sctl! : '%s'", clientname.sun_path); tmp = malloc(sizeof(struct ctlelem)); tmp->socket = s; LIST_INSERT_HEAD(&ctllisthead, tmp, entries); } #endif #ifdef ENABLE_EVENTS upnpevents_processfds(&readset, &writeset); #endif #ifdef ENABLE_NATPMP /* process NAT-PMP packets */ for(i=0; i<addr_count; i++) { if((snatpmp[i] >= 0) && FD_ISSET(snatpmp[i], &readset)) { ProcessIncomingNATPMPPacket(snatpmp[i]); } } #endif /* process SSDP packets */ if(sudp >= 0 && FD_ISSET(sudp, &readset)) { /*syslog(LOG_INFO, "Received UDP Packet");*/ ProcessSSDPRequest(sudp, (unsigned short)v.port); } #ifdef ENABLE_IPV6 if(sudpv6 >= 0 && FD_ISSET(sudpv6, &readset)) { syslog(LOG_INFO, "Received UDP Packet (IPv6)"); ProcessSSDPRequest(sudpv6, (unsigned short)v.port); } #endif #ifdef USE_IFACEWATCHER /* process kernel notifications */ if (sifacewatcher >= 0 && FD_ISSET(sifacewatcher, &readset)) ProcessInterfaceWatchNotify(sifacewatcher); #endif /* process active HTTP connections */ /* LIST_FOREACH macro is not available under linux */ for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if(e->socket >= 0) { if(FD_ISSET(e->socket, &readset) || FD_ISSET(e->socket, &writeset)) { Process_upnphttp(e); } } } /* process incoming HTTP connections */ if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) { int shttp; socklen_t clientnamelen; #ifdef ENABLE_IPV6 struct sockaddr_storage clientname; clientnamelen = sizeof(struct sockaddr_storage); #else struct sockaddr_in clientname; clientnamelen = sizeof(struct sockaddr_in); #endif shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); if(shttp<0) { /* ignore EAGAIN, EWOULDBLOCK, EINTR, we just try again later */ if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) syslog(LOG_ERR, "accept(http): %m"); } else { struct upnphttp * tmp = 0; char addr_str[64]; sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str)); syslog(LOG_INFO, "HTTP connection from %s", addr_str); /* Create a new upnphttp object and add it to * the active upnphttp object list */ tmp = New_upnphttp(shttp); if(tmp) { #ifdef ENABLE_IPV6 if(clientname.ss_family == AF_INET) { tmp->clientaddr = ((struct sockaddr_in *)&clientname)->sin_addr; } else if(clientname.ss_family == AF_INET6) { struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&clientname; if(IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) { memcpy(&tmp->clientaddr, &addr->sin6_addr.s6_addr[12], 4); } else { tmp->ipv6 = 1; memcpy(&tmp->clientaddr_v6, &addr->sin6_addr, sizeof(struct in6_addr)); } } #else tmp->clientaddr = clientname.sin_addr; #endif LIST_INSERT_HEAD(&upnphttphead, tmp, entries); } else { syslog(LOG_ERR, "New_upnphttp() failed"); close(shttp); } } } #ifdef ENABLE_NFQUEUE /* process NFQ packets */ if(nfqh >= 0 && FD_ISSET(nfqh, &readset)) { /* syslog(LOG_INFO, "Received NFQUEUE Packet");*/ ProcessNFQUEUE(nfqh); } #endif /* delete finished HTTP connections */ for(e = upnphttphead.lh_first; e != NULL; ) { next = e->entries.le_next; if(e->state >= EToDelete) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } e = next; } } /* end of main loop */ shutdown: /* close out open sockets */ while(upnphttphead.lh_first != NULL) { e = upnphttphead.lh_first; LIST_REMOVE(e, entries); Delete_upnphttp(e); } if (sudp >= 0) close(sudp); if (shttpl >= 0) close(shttpl); #ifdef ENABLE_IPV6 if (sudpv6 >= 0) close(sudpv6); #endif #ifdef USE_IFACEWATCHER if(sifacewatcher >= 0) close(sifacewatcher); #endif #ifdef ENABLE_NATPMP for(i=0; i<addr_count; i++) { if(snatpmp[i]>=0) { close(snatpmp[i]); snatpmp[i] = -1; } } #endif #ifdef USE_MINIUPNPDCTL if(sctl>=0) { close(sctl); sctl = -1; if(unlink("/var/run/miniupnpd.ctl") < 0) { syslog(LOG_ERR, "unlink() %m"); } } #endif if (GETFLAG(ENABLEUPNPMASK)) { #ifndef ENABLE_IPV6 if(SendSSDPGoodbye(snotify, addr_count) < 0) #else if(SendSSDPGoodbye(snotify, addr_count * 2) < 0) #endif { syslog(LOG_ERR, "Failed to broadcast good-bye notifications"); } #ifndef ENABLE_IPV6 for(i = 0; i < addr_count; i++) #else for(i = 0; i < addr_count * 2; i++) #endif close(snotify[i]); } if(pidfilename && (unlink(pidfilename) < 0)) { syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename); } /* delete lists */ while(lan_addrs.lh_first != NULL) { lan_addr = lan_addrs.lh_first; LIST_REMOVE(lan_addrs.lh_first, list); free(lan_addr); } #ifdef ENABLE_NATPMP free(snatpmp); #endif free(snotify); closelog(); #ifndef DISABLE_CONFIG_FILE freeoptions(); #endif return 0; }