blob: b0bb1a8268633b5970c6cc638eb0d3d0bbadddde [file] [log] [blame]
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "mosys/alloc.h"
#include "mosys/big_lock.h"
#include "mosys/command_list.h"
#include "mosys/globals.h"
#include "mosys/kv_pair.h"
#include "mosys/log.h"
#include "mosys/output.h"
#include "mosys/platform.h"
static void usage(void)
{
printf("usage: %s [options] [commands]\n\n"
" Options:\n"
" -k print data in key=value format\n"
" -l print data in long format\n"
" -s [key] print value for single key\n"
" -v verbose (can be used multiple times)\n"
" -f force (ignore mosys lock, sanity checks, etc)\n"
" -t display command tree for detected platform\n"
" -S print supported platform IDs\n"
" -p [id] specify platform id (bypass auto-detection)\n"
" -h print this help\n"
" -V print version\n"
"\n", PROGRAM);
}
static void sub_list(struct platform_cmd *sub)
{
struct platform_cmd *_sub;
if (!sub)
return;
printf(" Commands:\n");
for (_sub = sub->arg.sub; _sub && _sub->name; _sub++) {
if (_sub->desc)
printf(" %-12s %s\n", _sub->name, _sub->desc);
}
printf("\n");
}
static int sub_main(struct platform_intf *intf,
struct platform_cmd *sub, int argc, char **argv)
{
struct platform_cmd *cmd;
if (!intf || !sub) {
errno = ENOSYS;
return -1;
}
switch (sub->type) {
case ARG_TYPE_GETTER:
case ARG_TYPE_SETTER:
if (!sub->arg.func) {
lprintf(LOG_ERR, "Undefined Function\n");
return -1;
}
/* this is a function, it might have more args */
if (argc > 0 && strcmp(argv[0], "help") == 0) {
platform_cmd_usage(sub);
return 0;
}
/* run command handler */
return sub->arg.func(intf, sub, argc, argv);
case ARG_TYPE_SUB:
if (argc == 0) {
sub_list(sub);
return -1; /* generic error */
}
/* this is a sub-command, we must have some more args */
if (argc == 0 || strcmp(argv[0], "help") == 0) {
sub_list(sub);
return 0;
}
/* search for matching sub-command */
for (cmd = sub->arg.sub; cmd && cmd->name; cmd++) {
if (strlen(cmd->name) != strlen(argv[0]))
continue;
if (strcmp(cmd->name, argv[0]) == 0) {
lprintf(LOG_DEBUG, "Subcommand %s (%s)\n",
cmd->name, cmd->desc);
return sub_main(intf, cmd, argc - 1,
&(argv[1]));
}
}
break;
default:
lprintf(LOG_ERR, "Unknown subcommand type\n");
return -1;
}
lprintf(LOG_WARNING, "Command not found\n\n");
sub_list(sub);
errno = EINVAL; /* unknown or invalid subcommand argument */
return -1;
}
static int intf_main(struct platform_intf *intf, int argc, char **argv)
{
struct platform_cmd **_sub, *sub;
int do_list = 0;
int ret = 0;
if (!intf || !intf->sub) {
lprintf(LOG_ERR, "No commands defined for this platform\n");
errno = ENOSYS;
return -1;
}
if (argc == 0 || strcmp(argv[0], "help") == 0) {
do_list = 1;
usage();
printf(" Commands:\n");
}
/* go through subcommand list for this interface */
for (_sub = intf->sub; _sub && *_sub; _sub++) {
sub = *_sub;
if (do_list) {
/*FIXME: if the intf had a main sub, call sub_list */
printf(" %-12s %s\n", sub->name, sub->desc);
continue;
}
lprintf(LOG_DEBUG, "Command: %s (%s)\n", sub->name, argv[0]);
/* is this sub-command is the one they asked for? */
if (strlen(sub->name) != strlen(argv[0]))
continue;
if (strcmp(sub->name, argv[0]) == 0) {
lprintf(LOG_DEBUG, "Found command %s (%s)\n",
sub->name, sub->desc);
return sub_main(intf, sub, argc - 1, &(argv[1]));
}
}
if (do_list) {
printf("\n");
if (argc == 0)
ret = -1;
return ret;
}
lprintf(LOG_WARNING, "Command not found\n\n");
errno = ENOSYS;
return intf_main(intf, 0, NULL); /* trigger a help listing */
}
#define LOCK_TIMEOUT_SECS 180
int mosys_main(int argc, char **argv)
{
int rc, errsv;
int argflag;
int verbose = 0;
int force_lock = 0;
int print_platforms_opt = 0;
int showtree = 0;
char *p_opt = NULL;
struct platform_intf *intf;
enum kv_pair_style style = KV_STYLE_VALUE;
while ((argflag = getopt(argc, argv, "klvftSs:p:Vh")) > 0) {
switch (argflag) {
case 'k':
style = KV_STYLE_PAIR;
break;
case 'l':
style = KV_STYLE_LONG;
break;
case 's':
style = KV_STYLE_SINGLE;
if (!optarg) {
usage();
exit(EXIT_FAILURE);
}
kv_set_single_key(optarg);
break;
case 'v':
verbose++;
break;
case 'f':
force_lock = 1;
break;
case 't':
showtree = 1;
break;
case 'S':
print_platforms_opt = 1;
break;
case 'p':
p_opt = optarg;
break;
case 'V':
printf("mosys version %s\n", VERSION);
exit(EXIT_SUCCESS);
case 'h':
usage();
exit(EXIT_SUCCESS);
default:
usage();
exit(EXIT_FAILURE);
}
}
mosys_set_kv_pair_style(style);
if (print_platforms_opt)
return print_platforms();
/*
* Init the logging system and the default log output file (stderr).
* Anything logged with a level above CONFIG_LOGLEVEL will not be logged
* at all. We use the number of "-v" arguments on the commandline as
* a bias against the default threshold. The more times you use
* "-v", the greater your logging level becomes, and the more
* information will be printed.
*/
mosys_log_init(PROGRAM, CONFIG_LOGLEVEL+verbose, NULL);
#if defined(CONFIG_USE_IPC_LOCK)
/* try to get lock */
if (!force_lock && (mosys_acquire_big_lock(LOCK_TIMEOUT_SECS) < 0)) {
rc = -1;
goto do_exit_1;
}
#endif
/* set the global verbosity level */
mosys_set_verbosity(CONFIG_LOGLEVEL+verbose);
/* try to identify the platform */
intf = mosys_platform_setup(p_opt);
if (!intf) {
lprintf(LOG_ERR, "Platform not supported\n");
rc = -1;
goto do_exit_2;
}
lprintf(LOG_DEBUG, "Platform: %s\n", intf->name);
if (showtree) {
print_tree(intf);
rc = 0;
goto do_exit_3;
}
/* run command */
errno = 0;
rc = intf_main(intf, argc - optind, &(argv[optind]));
errsv = errno;
if (rc < 0 && errsv == ENOSYS)
lprintf(LOG_ERR, "Command not supported on this platform\n");
/* clean up */
do_exit_3:
mosys_platform_destroy(intf);
do_exit_2:
#if defined(CONFIG_USE_IPC_LOCK)
mosys_release_big_lock();
#endif
do_exit_1:
mosys_log_halt();
if (rc < 0 && errsv > 0)
exit(errsv);
exit(rc);
}
int main(int argc, char **argv)
{
mosys_globals_init();
return mosys_main(argc, argv);
}