/**
 * servevent
 * @brief Generate a serviceable event and an entry to the servicelog
 *
 * @param refcode the SRN or SRC for the serviceable event
 * @param sev the severity of the event
 * @param text the description of the serviceable event
 * @param vpd a structure containing the VPD of the target
 * @param callouts a linked list of FRU callouts
 * @return key of the new servicelog entry
 */
uint32_t
servevent(char *refcode, int sev, char *text, struct dev_vpd *vpd,
	  struct sl_callout *callouts)
{
	struct servicelog *slog;
	struct sl_event *entry = NULL;
	struct sl_data_enclosure *encl = NULL;
	uint64_t key;
	int rc;

	if ((refcode == NULL) || (text == NULL) || (vpd == NULL))
		return 0;

	entry = (struct sl_event *)malloc(sizeof(struct sl_event));
	if (entry == NULL) {
		fprintf(stderr, "Out of memory\n");
		return 0;
	}
	memset(entry, 0, sizeof(struct sl_event));

	encl = (struct sl_data_enclosure *)malloc(
					sizeof(struct sl_data_enclosure));
	if (encl == NULL) {
		fprintf(stderr, "Out of memory\n");
		return 0;
	}
	memset(encl, 0, sizeof(struct sl_data_enclosure));

	entry->addl_data = encl;

	entry->time_event = time(NULL);
	entry->type = SL_TYPE_ENCLOSURE;
	entry->severity = sev;
	entry->disposition = SL_DISP_UNRECOVERABLE;
	entry->serviceable = 1;
	entry->call_home_status = SL_CALLHOME_CANDIDATE;
	entry->description = (char *)malloc(strlen(text) + 1);
	strcpy(entry->description, text);
	entry->refcode = (char *)malloc(strlen(refcode) + 1);
	strcpy(entry->refcode, refcode);
	encl->enclosure_model = (char *)malloc(strlen(vpd->mtm) + 1);
	strcpy(encl->enclosure_model, vpd->mtm);
	encl->enclosure_serial = (char *)malloc(strlen(vpd->sn) + 1);
	strcpy(encl->enclosure_serial, vpd->sn);

	entry->callouts = callouts;

	rc = servicelog_open(&slog, 0);
	if (rc != 0) {
		fprintf(stderr, "%s", servicelog_error(slog));
		return 0;
	}

	rc = servicelog_event_log(slog, entry, &key);
	servicelog_event_free(entry);
	servicelog_close(slog);

	if (rc != 0) {
		fprintf(stderr, "%s", servicelog_error(slog));
		return 0;
	}

	return key;
}
int
main(int argc, char *argv[])
{
    int argerr = 0;
    int flag;
    int index = 0;
    int rc = 0;
    servicelog *slog = NULL;
    struct sl_event *event = NULL;
    uint64_t event_id = 0;

    crm_log_init_quiet("notifyServicelogEvent", LOG_INFO, FALSE, TRUE, argc, argv);
    crm_set_options(NULL, "event_id ", long_options,
                    "Gets called upon events written to servicelog database");

    if (argc < 2) {
        argerr++;
    }

    while (1) {
        flag = crm_get_option(argc, argv, &index);
        if (flag == -1)
            break;

        switch (flag) {
            case '?':
            case '$':
                crm_help(flag, CRM_EX_OK);
                break;
            default:
                ++argerr;
                break;
        }
    }

    if (argc - optind != 1) {
        ++argerr;
    }

    if (argerr) {
        crm_help('?', CRM_EX_USAGE);
    }

    openlog("notifyServicelogEvent", LOG_NDELAY, LOG_USER);

    if (sscanf(argv[optind], U64T, &event_id) != 1) {
        crm_err("Error: could not read event_id from args!");

        rc = 1;
        goto cleanup;
    }

    if (event_id == 0) {
        crm_err("Error: event_id is 0!");

        rc = 1;
        goto cleanup;
    }

    rc = servicelog_open(&slog, 0);     /* flags is one of SL_FLAG_xxx */

    if (!slog) {
        crm_err("Error: servicelog_open failed, rc = %d", rc);

        rc = 1;
        goto cleanup;
    }

    if (slog) {
        rc = servicelog_event_get(slog, event_id, &event);
    }

    if (rc == 0) {
        STATUS status = STATUS_GREEN;
        const char *health_component = "#health-ipmi";
        const char *health_status = NULL;

        crm_debug("Event id = " U64T ", Log timestamp = %s, Event timestamp = %s",
                  event_id, ctime(&(event->time_logged)), ctime(&(event->time_event)));

        status = event2status(event);

        health_status = status2char(status);

        if (health_status) {
            gboolean rc;

            /* @TODO pass attrd_opt_remote when appropriate */
            rc = (attrd_update_delegate(NULL, 'v', NULL, health_component,
                                        health_status, NULL, NULL, NULL, NULL,
                                        attrd_opt_none) > 0);
            crm_debug("Updating attribute ('%s', '%s') = %d",
                      health_component, health_status, rc);
        } else {
            crm_err("Error: status2char failed, status = %d", status);
            rc = 1;
        }
    } else {
        crm_err("Error: servicelog_event_get failed, rc = %d", rc);
    }

  cleanup:
    if (event) {
        servicelog_event_free(event);
    }

    if (slog) {
        servicelog_close(slog);
    }

    closelog();

    return rc;
}
/**
 * main
 * @brief Parse command line args process database
 *
 * @param argc the number of command-line arguments
 * @param argv array of command-line arguments
 * @return exit status: 0 for normal exit, 1 for usage error, >1 for other error
 */
int
main(int argc, char *argv[])
{
	struct servicelog *slog;
	int rc;
	struct sl_event *event, *events;
	struct sl_repair_action *repair, *repairs;
	struct sl_notify *notify, *notifications;
	int option_index, action=ACTION_UNSPECIFIED;
	int flag_force=0;
	int age = 60;	/* default age for --clean */
	int platform = 0;
	char buf[124];
	char *tmp;
	char *next_char;
	uint32_t num=0, num_repaired=0, num_unrepaired=0, num_info=0, num_ra=0;
	uint32_t span;
	time_t now;

	cmd = argv[0];

	platform = get_platform();
	switch (platform) {
	case PLATFORM_UNKNOWN:
	case PLATFORM_POWERNV:
		fprintf(stderr, "%s: is not supported on the %s platform\n",
					cmd, __power_platform_name(platform));
		exit(1);
	}

	if (argc <= 1) {
		print_usage();
		exit(0);
	}

	for (;;) {
		option_index = 0;
		rc = getopt_long(argc, argv, ARG_LIST, long_options,
				&option_index);

		if (rc == -1)
			break;

		switch (rc) {
		case 's':
			if (action != ACTION_UNSPECIFIED)
				action = ACTION_TOOMANY;
			if (action != ACTION_TOOMANY)
				action = ACTION_STATUS;
			break;
		case 't':
			if (!optarg) {
				fprintf(stderr, "The --truncate option "
					"requires either \"events\" or "
					"\"notify\" as an argument.\n");
				print_usage();
				exit(1);
			}

			if (!strcmp(optarg, "events")) {
				if (action != ACTION_UNSPECIFIED)
					action = ACTION_TOOMANY;
				if (action != ACTION_TOOMANY)
					action = ACTION_TRUNCATE_EVENTS;
			}
			else if (!strcmp(optarg, "notify")) {
				if (action != ACTION_UNSPECIFIED)
					action = ACTION_TOOMANY;
				if (action != ACTION_TOOMANY)
					action = ACTION_TRUNCATE_NOTIFY;
			}
			else {
				fprintf(stderr, "The --truncate option "
					"requires either \"events\" or "
					"\"notify\" as an argument.\n");
				print_usage();
				exit(1);
			}
			break;
		case 'c':
			if (action != ACTION_UNSPECIFIED)
				action = ACTION_TOOMANY;
			if (action != ACTION_TOOMANY)
				action = ACTION_CLEAN;
			break;
		case 'a':
			age = (int)strtoul(optarg, &next_char, 10);
			if (optarg[0] == '\0' || *next_char != '\0' ||
								age < 0) {
				print_usage();
				exit(1);
			}
			break;
		case 'f':
			flag_force = 1;
			break;
		case 'h':	/* help */
			print_usage();
			exit(0);
		case '?':
			print_usage();
			exit(1);
		default:
			printf("Invalid argument: %s\n", optarg);
			print_usage();
			exit(1);
		}
	}

	if (optind < argc) {
		print_usage();
		exit(1);
	}

	/* Command-line validation */
	if (action == ACTION_UNSPECIFIED) {
		fprintf(stderr, "One of the action options is required.\n");
		print_usage();
		exit(1);
	}

	if (action == ACTION_TOOMANY) {
		fprintf(stderr, "Only one of the action options may be "
			"specified.\n");
		print_usage();
		exit(1);
	}


	switch (action) {

	case ACTION_STATUS:
		rc = servicelog_open(&slog, 0);
		if (rc != 0) {
			fprintf(stderr, "%s: Could not open servicelog "
					"database.\n%s\n",
					argv[0], servicelog_error(slog));
			exit(2);
		}

		rc = servicelog_event_query(slog, "", &events);
		if (rc != 0) {
			fprintf(stderr, "%s\n", servicelog_error(slog));
			servicelog_close(slog);
			exit(2);
		}

		for (event = events; event; event = event->next) {
			num++; // total event count
			if (event->serviceable && (event->repair > 0))
				num_repaired++;
			else if (event->serviceable)
				num_unrepaired++;
			else
				num_info++; // informational events
		}

		servicelog_event_free(events);

		// Now need to query repair actions:
		rc = servicelog_repair_query(slog, "", &repairs);
		if (rc != 0) {
			fprintf(stderr, "%s\n", servicelog_error(slog));
			servicelog_close(slog);
			exit(2);
		}

		for (repair = repairs; repair; repair = repair->next)
			num_ra++;
		servicelog_repair_free(repairs);

		servicelog_close(slog);

		printf("%-39s%10u\n", "Logged events:", num);
		printf("    %-35s%10u\n", "unrepaired serviceable events:",
		       num_unrepaired);
		printf("    %-35s%10u\n", "repaired serviceable events:",
		       num_repaired);
		printf("    %-35s%10u\n", "informational events:", num_info);
		printf("    %-35s%10u\n", "repair actions:", num_ra);
		break;


	case ACTION_TRUNCATE_EVENTS:
		if (geteuid() != 0) // Check to see if user is root
		{
			printf("Must be root to truncate the database!\n");
			exit(2);
		}

		num = 0;

		if (!flag_force) {
			printf("Are you certain you wish to delete ALL events "
			       "from the servicelog?\n");
			printf("Enter 'yes' to continue > ");
			tmp = fgets(buf, 80, stdin);
			if (!tmp)
				exit(2);

			if (strcasecmp(buf, "yes\n")) {
				printf("Operation cancelled.\n");
				exit(4);
			}
		}

		rc = servicelog_open(&slog, SL_FLAG_ADMIN);
		if (rc != 0) {
			fprintf(stderr, "%s: Could not open servicelog "
					"database.\n%s\n",
					argv[0], servicelog_error(slog));
			exit(2);
		}
		rc = servicelog_event_query(slog, "", &events);
		if (rc != 0) {
			fprintf(stderr, "%s\n", servicelog_error(slog));
			servicelog_close(slog);
			exit(2);
		}

		for (event = events; event; event = event->next) {
			num++;
			servicelog_event_delete(slog, event->id);
		}

		servicelog_event_free(events);

		// Delete repair actions as well.
		rc = servicelog_repair_query(slog, "", &repairs);
		if (rc != 0) {
			fprintf(stderr, "%s\n", servicelog_error(slog));
			servicelog_close(slog);
			exit(2);
		}

		for (repair = repairs; repair; repair = repair->next) {
			num_ra++;
			servicelog_repair_delete(slog, repair->id);
		}
		servicelog_repair_free(repairs);

		printf("Deleted %u records.\n", num + num_ra);
		servicelog_close(slog);
		break;

	case ACTION_TRUNCATE_NOTIFY:
		if (geteuid() != 0) // Check to see if user is root
		{
			printf("Must be root to truncate the database!\n");
			exit(2);
		}

		num = 0;

		if (!flag_force) {
			printf("Are you certain you wish to delete ALL "
					"notification tools from the servicelog?\n");
			printf("Enter 'yes' to continue > ");
			tmp = fgets(buf, 80, stdin);
			if (!tmp)
				exit(2);

			if (strcasecmp(buf, "yes\n")) {
				printf("Operation cancelled.\n");
				exit(4);
			}
		}

		rc = servicelog_open(&slog, SL_FLAG_ADMIN);
		if (rc != 0) {
			fprintf(stderr, "%s: Could not open servicelog "
					"database.\n%s\n",
					argv[0], servicelog_error(slog));
			exit(2);
		}
		rc = servicelog_notify_query(slog, "", &notifications);
		if (rc != 0) {
			fprintf(stderr, "%s\n", servicelog_error(slog));
			servicelog_close(slog);
			exit(2);
		}

		for (notify = notifications; notify; notify = notify->next) {
			num++;
			servicelog_notify_delete(slog, notify->id);
		}
		servicelog_notify_free(notifications);
		servicelog_close(slog);

		printf("Deleted %u records.\n", num);
		break;

	case ACTION_CLEAN:
		if (geteuid() != 0) { // Check to see if user is root
			printf("Must be root to purge older events "
			       "in the database!\n");
			exit(2);
		}

		if (!flag_force) {
			printf("Are you certain you wish to perform the "
			       "following tasks?\n"
			       " - Delete all repaired serviceable events\n"
			       " - Delete all informational events older than "
			       "%d days\n"
			       " - Delete all repair actions older than "
			       "%d days\n"
			       " - Delete anything older than 1 year\n",
			       age, age);
			printf("Enter 'yes' to continue > ");
			tmp = fgets(buf, 80, stdin);
			if (!tmp)
				exit(2);

			if (strcasecmp(buf, "yes\n")) {
				printf("Operation cancelled.\n");
				break;
			}
		}

		rc = servicelog_open(&slog, 0);
		if (rc != 0) {
			fprintf(stderr, "%s: Could not open servicelog "
					"database.\n%s\n",
					argv[0], servicelog_error(slog));
			exit(2);
		}

		now = time(NULL);
		span = age * SECONDS_IN_DAY;

		rc = servicelog_event_query(slog, "", &events);
		if (rc != 0) {
			fprintf(stderr, "%s\n", servicelog_error(slog));
			servicelog_close(slog);
			exit(2);
		}

		for (event = events; event; event = event->next) {
			if (event->serviceable && event->closed) {
				num_repaired++;
				servicelog_event_delete(slog, event->id);
			}
			else if (!event->serviceable &&
				 (event->time_logged + span) < now) {
				num_info++;
				servicelog_event_delete(slog, event->id);
			}
			else if ((event->time_logged + SECONDS_IN_YEAR) < now) {
				num++;
				servicelog_event_delete(slog, event->id);
			}
		}
		servicelog_event_free(events);

		/* Delete repair actions which are older than age */
		rc = servicelog_repair_query(slog, "", &repairs);
		if (rc != 0) {
			fprintf(stderr, "%s\n", servicelog_error(slog));
			servicelog_close(slog);
			exit(2);
		}
		for (repair = repairs; repair; repair = repair->next) {
			if ((repair->time_logged + span) < now ) {
				num_ra++;
				servicelog_repair_delete(slog, repair->id);
			}
		}
		servicelog_repair_free(repairs);
		servicelog_close(slog);

		printf("Removed %u repaired serviceable events.\n",
		       num_repaired);
		printf("Removed %u informational events older than %d days.\n",
		       num_info, age);
		printf("Removed %u repair actions older than %d days.\n",
		       num_ra, age);
		printf("Removed %u other events older than one year.\n", num);
		break;

	default:
		fprintf(stderr, "Internal error; unknown action %d\n", action);
		exit(3);
	}

	exit(0);
}