1 /*
2  # This file is part of the Astrometry.net suite.
3  # Licensed under a 3-clause BSD style license - see LICENSE
4  */
5 
6 /**
7  * Accepts an augmented xylist that describes a field or set of fields to solve.
8  * Reads a config file to find local indices, and merges information about the
9  * indices with the job description to create an input file for 'onefield'.  Runs
10  * and merges the results.
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #include <sys/time.h>
20 #include <time.h>
21 #include <libgen.h>
22 #include <getopt.h>
23 #include <dirent.h>
24 #include <assert.h>
25 #include <glob.h>
26 
27 // Some systems (Solaris) don't have these glob symbols.  Don't really need.
28 #ifndef GLOB_BRACE
29 #define GLOB_BRACE 0
30 #endif
31 #ifndef GLOB_TILDE
32 #define GLOB_TILDE 0
33 #endif
34 
35 #include "os-features.h"
36 #include "tic.h"
37 #include "fileutils.h"
38 #include "ioutils.h"
39 #include "bl.h"
40 #include "an-bool.h"
41 #include "solver.h"
42 #include "math.h"
43 #include "fitsioutils.h"
44 #include "solverutils.h"
45 #include "onefield.h"
46 #include "log.h"
47 #include "errors.h"
48 #include "engine.h"
49 #include "an-opts.h"
50 #include "gslutils.h"
51 
52 #include "datalog.h"
53 
54 static an_option_t myopts[] = {
55     {'h', "help", no_argument, NULL, "print this help"},
56     {'v', "verbose", no_argument, NULL, "+verbose"},
57     {'c', "config",  required_argument, "file",
58      "Use this config file (default: \"astrometry.cfg\" in the directory ../etc/ relative to the directory containing the \"astrometry-engine\" executable); 'none' for no config file"},
59     {'d', "base-dir",  required_argument, "dir",
60      "set base directory of all output filenames."},
61     {'C', "cancel",  required_argument, "file",
62      "quit solving if this file appears" },
63     {'s', "solved",  required_argument, "file",
64      "write to this file when a field is solved"},
65     {'E', "to-stderr", no_argument, NULL,
66      "send log message to stderr"},
67     {'f', "inputs-from", required_argument, "file",
68      "read input filenames from the given file, \"-\" for stdin"},
69     {'i', "index", required_argument, "file(s)",
70      "use the given index files (in addition to any specified in the config file); put in quotes to use wildcards, eg: \" -i 'index-*.fits' \""},
71     {'p', "in-parallel", no_argument, NULL,
72      "run the index files in parallel"},
73     {'D', "data-log file", required_argument, "file",
74      "log data to the given filename"},
75     {'j', "job-id", required_argument, "jobid",
76      "IGNORED; purely to allow process to contain the job id!"},
77 };
78 
print_help(const char * progname,bl * opts)79 static void print_help(const char* progname, bl* opts) {
80     printf("Usage:   %s [options] <augmented xylist (axy) file(s)>\n", progname);
81     opts_print_help(opts, stdout, NULL, NULL);
82 }
83 
84 FILE* datalogfid = NULL;
close_datalogfid()85 static void close_datalogfid() {
86     if (datalogfid) {
87         data_log_end();
88         if (fclose(datalogfid)) {
89             SYSERROR("Failed to close data log file");
90         }
91     }
92 }
93 
main(int argc,char ** args)94 int main(int argc, char** args) {
95     char* default_configfn = "astrometry.cfg";
96     char* default_config_path = "../etc";
97 
98     int c;
99     char* configfn = NULL;
100     int i;
101     engine_t* engine;
102     char* mydir = NULL;
103     char* basedir = NULL;
104     char* me;
105     anbool help = FALSE;
106     sl* strings = sl_new(4);
107     char* cancelfn = NULL;
108     char* solvedfn = NULL;
109     int loglvl = LOG_MSG;
110     anbool tostderr = FALSE;
111     char* infn = NULL;
112     FILE* fin = NULL;
113     anbool fromstdin = FALSE;
114 
115     bl* opts = opts_from_array(myopts, sizeof(myopts)/sizeof(an_option_t), NULL);
116     sl* inds = sl_new(4);
117 
118     char* datalog = NULL;
119 
120     engine = engine_new();
121 
122     while (1) {
123         c = opts_getopt(opts, argc, args);
124         if (c == -1)
125             break;
126         switch (c) {
127 	case 'j':
128 	    break;
129         case 'D':
130             datalog = optarg;
131             break;
132         case 'p':
133             engine->inparallel = TRUE;
134             break;
135         case 'i':
136             sl_append(inds, optarg);
137             break;
138         case 'd':
139             basedir = optarg;
140             break;
141         case 'f':
142             infn = optarg;
143             fromstdin = streq(infn, "-");
144             break;
145         case 'E':
146             tostderr = TRUE;
147             break;
148         case 'h':
149             help = TRUE;
150             break;
151         case 'v':
152             loglvl++;
153             break;
154         case 's':
155             solvedfn = optarg;
156         case 'C':
157             cancelfn = optarg;
158             break;
159         case 'c':
160             configfn = strdup(optarg);
161             break;
162         case '?':
163             break;
164         default:
165             printf("Unknown flag %c\n", c);
166             exit( -1);
167         }
168     }
169 
170     if (optind == argc && !infn) {
171         // Need extra args: filename
172         printf("You must specify at least one input file!\n\n");
173         help = TRUE;
174     }
175     if (help) {
176         print_help(args[0], opts);
177         exit(0);
178     }
179     bl_free(opts);
180 
181     gslutils_use_error_system();
182 
183     log_init(loglvl);
184     if (tostderr)
185         log_to(stderr);
186 
187     if (datalog) {
188         datalogfid = fopen(datalog, "wb");
189         if (!datalogfid) {
190             SYSERROR("Failed to open data log file \"%s\" for writing", datalog);
191             return -1;
192         }
193         atexit(close_datalogfid);
194         data_log_init(100);
195         data_log_enable_all();
196         data_log_to(datalogfid);
197         data_log_start();
198     }
199 
200     if (infn) {
201         logverb("Reading input filenames from %s\n", (fromstdin ? "stdin" : infn));
202         if (!fromstdin) {
203             fin = fopen(infn, "rb");
204             if (!fin) {
205                 ERROR("Failed to open file %s for reading input filenames", infn);
206                 exit(-1);
207             }
208         } else
209             fin = stdin;
210     }
211 
212     // directory containing the 'engine' executable:
213     me = find_executable(args[0], NULL);
214     if (!me)
215         me = strdup(args[0]);
216     mydir = sl_append(strings, dirname(me));
217     free(me);
218 
219     // Read config file
220     if (!configfn) {
221         int i;
222         sl* trycf = sl_new(4);
223         sl_appendf(trycf, "%s/%s/%s", mydir, default_config_path, default_configfn);
224         // if I'm in /usr/bin, look for config file in /etc
225         if (streq(mydir, "/usr/bin")) {
226             sl_appendf(trycf, "/etc/%s", default_configfn);
227         }
228         sl_appendf(trycf, "%s/%s", mydir, default_configfn);
229         sl_appendf(trycf, "./%s", default_configfn);
230         sl_appendf(trycf, "./%s/%s", default_config_path, default_configfn);
231         for (i=0; i<sl_size(trycf); i++) {
232             char* cf = sl_get(trycf, i);
233             if (file_exists(cf)) {
234                 configfn = strdup(cf);
235                 logverb("Using config file \"%s\"\n", cf);
236                 break;
237             } else {
238                 logverb("Config file \"%s\" doesn't exist.\n", cf);
239             }
240         }
241         if (!configfn) {
242             char* cflist = sl_join(trycf, "\n  ");
243             logerr("Couldn't find config file: tried:\n  %s\n", cflist);
244             free(cflist);
245         }
246         sl_free2(trycf);
247     }
248 
249     if (!streq(configfn, "none")) {
250         if (engine_parse_config_file(engine, configfn)) {
251             logerr("Failed to parse (or encountered an error while interpreting) config file \"%s\"\n", configfn);
252             exit( -1);
253         }
254     }
255 
256     if (sl_size(inds)) {
257         // Expand globs.
258         for (i=0; i<sl_size(inds); i++) {
259             char* s = sl_get(inds, i);
260             glob_t myglob;
261             int flags = GLOB_TILDE | GLOB_BRACE;
262             if (glob(s, flags, NULL, &myglob)) {
263                 SYSERROR("Failed to expand wildcards in index-file path \"%s\"", s);
264                 exit(-1);
265             }
266             for (c=0; c<myglob.gl_pathc; c++) {
267                 if (engine_add_index(engine, myglob.gl_pathv[c])) {
268                     ERROR("Failed to add index \"%s\"", myglob.gl_pathv[c]);
269                     exit(-1);
270                 }
271             }
272             globfree(&myglob);
273         }
274     }
275 
276     if (!pl_size(engine->indexes)) {
277         logerr("\n\n"
278                "---------------------------------------------------------------------\n"
279                "You must list at least one index in the config file (%s)\n\n"
280                "See http://astrometry.net/use.html about how to get some index files.\n"
281                "---------------------------------------------------------------------\n"
282                "\n", configfn);
283         exit(-1);
284     }
285 
286     if (engine->minwidth <= 0.0 || engine->maxwidth <= 0.0) {
287         logerr("\"minwidth\" and \"maxwidth\" in the config file %s must be positive!\n", configfn);
288         exit(-1);
289     }
290 
291     free(configfn);
292 
293     if (!il_size(engine->default_depths)) {
294         parse_depth_string(engine->default_depths,
295                            "10 20 30 40 50 60 70 80 90 100 "
296                            "110 120 130 140 150 160 170 180 190 200");
297     }
298 
299     engine->cancelfn = cancelfn;
300     engine->solvedfn = solvedfn;
301 
302     i = optind;
303     while (1) {
304         char* jobfn;
305         job_t* job;
306         struct timeval tv1, tv2;
307 
308         if (infn) {
309             // Read name of next input file to be read.
310             logverb("\nWaiting for next input filename...\n");
311             jobfn = read_string_terminated(fin, "\n\r\0", 3, FALSE);
312             if (strlen(jobfn) == 0)
313                 break;
314         } else {
315             if (i == argc)
316                 break;
317             jobfn = args[i];
318             i++;
319         }
320         gettimeofday(&tv1, NULL);
321         logmsg("Reading file \"%s\"...\n", jobfn);
322         job = engine_read_job_file(engine, jobfn);
323         if (!job) {
324             ERROR("Failed to read job file \"%s\"", jobfn);
325             exit(-1);
326         }
327 
328 	if (basedir) {
329             logverb("Setting job's output base directory to %s\n", basedir);
330             job_set_output_base_dir(job, basedir);
331 	}
332 
333         if (engine_run_job(engine, job))
334             logerr("Failed to run_job()\n");
335 
336         job_free(job);
337         gettimeofday(&tv2, NULL);
338         logverb("Spent %g seconds on this field.\n", millis_between(&tv1, &tv2)/1000.0);
339     }
340 
341     engine_free(engine);
342     sl_free2(strings);
343     sl_free2(inds);
344 
345     if (fin && !fromstdin)
346         fclose(fin);
347 
348     return 0;
349 }
350