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