1 #include <lmdump.h>
2 #include <dump.h>
3 #include <diagnose.h>
4 #include <backup.h>
5 #include <repair.h>
6 #include <string_lib.h>
7 #include <logging.h>
8 #include <man.h>
9 
print_version()10 static void print_version()
11 {
12     printf("cf-check BETA version %s\n", VERSION);
13 }
14 
print_help()15 static void print_help()
16 {
17     printf(
18         "\n"
19         "cf-check:\n"
20         "\tUtility for diagnosis and repair of local CFEngine databases.\n"
21         "\tThis BETA version of the tool is for testing purposes only.\n"
22         "\n"
23         "Commands:\n"
24         "\tdump - Print the contents of a database file\n"
25         "\tdiagnose - Assess the health of one or more database files\n"
26         "\tbackup - Copy database files to a timestamped folder\n"
27         "\trepair - Diagnose, then backup and delete any corrupt databases\n"
28         "\tversion - Print version information\n"
29         "\thelp - Print this help menu\n"
30         "\n"
31         "Usage:\n"
32         "\t$ cf-check <command> [options] [file ...]\n"
33         "\n"
34         "Examples:\n"
35         "\t$ cf-check dump " WORKDIR "/state/cf_lastseen.lmdb\n"
36         "\t$ cf-check lmdump -a " WORKDIR "/state/cf_lastseen.lmdb\n"
37         "\t$ cf-check diagnose\n"
38         "\t$ cf-check repair\n"
39         "\n");
40 }
41 
42 static const char *const CF_CHECK_SHORT_DESCRIPTION =
43     "Utility for diagnosis and repair of local CFEngine databases.";
44 
45 static const char *const CF_CHECK_MANPAGE_LONG_DESCRIPTION =
46     "cf-check does not evaluate policy or rely on the integrity of the\n"
47     "databases. It is intended to be able to detect and repair a corrupt\n"
48     "database.";
49 
50 static const Description COMMANDS[] =
51 {
52     {"help",     "Prints general help or per topic",
53                  "cf-check help [command]"},
54     {"diagnose", "Assess the health of one or more database files",
55                  "cf-check diagnose"},
56     {"backup",   "Backup database files to a timestamped folder",
57                  "cf-check backup"},
58     {"repair",   "Diagnose, then backup and delete any corrupt databases",
59                  "cf-check repair"},
60     {"dump",     "Print the contents of a database file",
61                  "cf-check dump " WORKDIR "/state/cf_lastseen.lmdb"},
62     {"lmdump",   "LMDB database dumper (deprecated)",
63                  "cf-check lmdump -a " WORKDIR "/state/cf_lastseen.lmdb"},
64     {NULL, NULL, NULL}
65 };
66 
67 static const struct option OPTIONS[] =
68 {
69     {"help",        optional_argument,  0, 'h'},
70     {"manpage",     no_argument,        0, 'M'},
71     {"version",     no_argument,        0, 'V'},
72     {"debug",       no_argument,        0, 'd'},
73     {"verbose",     no_argument,        0, 'v'},
74     {"log-level",   required_argument,  0, 'g'},
75     {"inform",      no_argument,        0, 'I'},
76     {NULL,          0,                  0, '\0'}
77 };
78 
79 static const char *const HINTS[] =
80 {
81     "Print the help message",
82     "Print the man page",
83     "Output the version of the software",
84     "Enable debugging output",
85     "Enable verbose output",
86     "Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'",
87     "Enable basic information output",
88     NULL
89 };
90 
CFCheckHelpTopic(const char * topic)91 static int CFCheckHelpTopic(const char *topic)
92 {
93     assert(topic != NULL);
94     bool found = false;
95     for (int i = 0; COMMANDS[i].name != NULL; ++i)
96     {
97         if (strcmp(COMMANDS[i].name, topic) == 0)
98         {
99             printf("Command:     %s\n", COMMANDS[i].name);
100             printf("Usage:       %s\n", COMMANDS[i].usage);
101             printf("Description: %s\n", COMMANDS[i].description);
102             found = true;
103             break;
104         }
105     }
106 
107     // Add more detailed explanation here if necessary:
108     if (strcmp("help", topic) == 0)
109     {
110         printf("\nYou did it, you used the help command!\n");
111     }
112     else
113     {
114         if (!found)
115         {
116             printf("Unknown help topic: '%s'\n", topic);
117             return EXIT_FAILURE;
118         }
119     }
120     return EXIT_SUCCESS;
121 }
122 
main(int argc,const char * const * argv)123 int main(int argc, const char *const *argv)
124 {
125     if (StringEndsWith(argv[0], "lmdump"))
126     {
127         // Compatibility mode; act like lmdump if symlinked or renamed:
128         return lmdump_main(argc, argv);
129     }
130 
131     // When run separately it makes sense for cf-check to have INFO messages
132     LogSetGlobalLevel(LOG_LEVEL_INFO);
133     // In agent, NOTICE log level is default, and cf-check functions
134     // will print less information
135 
136     if (argc < 2)
137     {
138         print_help();
139         Log(LOG_LEVEL_ERR, "No command given");
140         return EXIT_FAILURE;
141     }
142 
143     int c = 0;
144     int start_index = 1;
145     const char *optstr = "+hMg:dvI"; // + means stop for non opt arg. :)
146     while ((c = getopt_long(argc, (char *const *) argv, optstr, OPTIONS, &start_index))
147             != -1)
148     {
149         switch (c)
150         {
151             case 'd':
152             {
153                 LogSetGlobalLevel(LOG_LEVEL_DEBUG);
154                 break;
155             }
156             case 'v':
157             {
158                 LogSetGlobalLevel(LOG_LEVEL_VERBOSE);
159                 break;
160             }
161             case 'I':
162             {
163                 LogSetGlobalLevel(LOG_LEVEL_INFO);
164                 break;
165             }
166             case 'g':
167             {
168                 LogSetGlobalLevelArgOrExit(optarg);
169                 break;
170             }
171             case 'V':
172             {
173                 print_version();
174                 return EXIT_SUCCESS;
175                 break;
176             }
177             case 'h':
178             {
179                 print_help();
180                 return EXIT_SUCCESS;
181                 break;
182             }
183             case 'M':
184             {
185                 Writer *out = FileWriter(stdout);
186                 ManPageWrite(out, "cf-check", time(NULL),
187                              CF_CHECK_SHORT_DESCRIPTION,
188                              CF_CHECK_MANPAGE_LONG_DESCRIPTION,
189                              OPTIONS, HINTS,
190                              NULL, false,
191                              true);
192                 FileWriterDetach(out);
193                 return EXIT_SUCCESS;
194                 break;
195             }
196             default:
197             {
198                 return EXIT_FAILURE;
199                 break;
200             }
201         }
202     }
203     const char *const *const cmd_argv = argv + optind;
204     int cmd_argc = argc - optind;
205     const char *command = cmd_argv[0];
206 
207     if (StringEqual_IgnoreCase(command, "lmdump"))
208     {
209         return lmdump_main(cmd_argc, cmd_argv);
210     }
211     if (StringEqual_IgnoreCase(command, "dump"))
212     {
213         return dump_main(cmd_argc, cmd_argv);
214     }
215     if (StringEqual_IgnoreCase(command, "diagnose"))
216     {
217         return diagnose_main(cmd_argc, cmd_argv);
218     }
219     if (StringEqual_IgnoreCase(command, "backup"))
220     {
221         return backup_main(cmd_argc, cmd_argv);
222     }
223     if (StringEqual_IgnoreCase(command, "repair") ||
224         StringEqual_IgnoreCase(command, "remediate"))
225     {
226         return repair_main(cmd_argc, cmd_argv);
227     }
228     if (StringEqual_IgnoreCase(command, "help"))
229     {
230         if (cmd_argc > 2)
231         {
232             Log(LOG_LEVEL_ERR, "help takes exactly 0 or 1 arguments");
233             return EXIT_FAILURE;
234         }
235         else if (cmd_argc <= 1)
236         {
237             print_help();
238         }
239         else
240         {
241             assert(cmd_argc == 2);
242             CFCheckHelpTopic(cmd_argv[1]);
243         }
244         return EXIT_SUCCESS;
245     }
246     if (StringEqual_IgnoreCase(command, "version"))
247     {
248         print_version();
249         return EXIT_SUCCESS;
250     }
251 
252     print_help();
253     Log(LOG_LEVEL_ERR, "Unrecognized command: '%s'", command);
254     return EXIT_FAILURE;
255 }
256