pwrctl: add pgood state switching application

pgood_wait simply blocks until the power state changes to
the requested state.

The intent is for use from a systemd unit similar to nm-online.

Change-Id: Icf1e18f5d47d6eb2fc424b4614fd196ef3dfd6ee
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/op-pwrctl/pgood_wait/.gitignore b/op-pwrctl/pgood_wait/.gitignore
new file mode 100644
index 0000000..fbc938c
--- /dev/null
+++ b/op-pwrctl/pgood_wait/.gitignore
@@ -0,0 +1 @@
+pgood_wait
diff --git a/op-pwrctl/pgood_wait/Makefile b/op-pwrctl/pgood_wait/Makefile
new file mode 100644
index 0000000..c54382e
--- /dev/null
+++ b/op-pwrctl/pgood_wait/Makefile
@@ -0,0 +1,5 @@
+BINS=pgood_wait
+BIN_SUFFIX=""
+LDLIBS=-lmapper
+include ../../sdbus.mk
+include ../../rules.mk
diff --git a/op-pwrctl/pgood_wait/pgood_wait.c b/op-pwrctl/pgood_wait/pgood_wait.c
new file mode 100644
index 0000000..949776d
--- /dev/null
+++ b/op-pwrctl/pgood_wait/pgood_wait.c
@@ -0,0 +1,183 @@
+/**
+ * Copyright © 2016 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
+#include <mapper.h>
+
+static void quit(int r, void *loop)
+{
+	sd_event_exit((sd_event *)loop, r);
+}
+
+static int callback(sd_bus_message *m, void *user, sd_bus_error *error)
+{
+	sd_event *loop = user;
+	int r;
+	char *property = NULL;
+
+	r = sd_bus_message_skip(m, "s");
+	if (r < 0) {
+		fprintf(stderr, "Error skipping message fields: %s\n",
+				strerror(-r));
+		quit(r, loop);
+		return r;
+	}
+
+	r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+	if (r < 0) {
+		fprintf(stderr, "Error entering container: %s\n",
+				strerror(-r));
+		quit(r, loop);
+		return r;
+	}
+
+	while((r = sd_bus_message_enter_container(
+					m,
+					SD_BUS_TYPE_DICT_ENTRY,
+					"sv")) > 0) {
+		r = sd_bus_message_read(m, "s", &property);
+		if (r < 0) {
+			fprintf(stderr, "Error reading message: %s\n",
+					strerror(-r));
+			quit(r, loop);
+			return r;
+		}
+
+		if(strcmp(property, "pgood"))
+			continue;
+
+		quit(0, loop);
+		break;
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	static const char *matchfmt =
+		"type='signal',"
+		"interface='org.freedesktop.DBus.Properties',"
+		"member='PropertiesChanged',"
+		"arg0='org.openbmc.control.Power',"
+		"path='%s',"
+		"sender='%s'";
+	static const char *usage =
+		"Usage: %s OBJECTPATH on|off\n";
+	static const size_t LEN = 256;
+
+	sd_bus *conn = NULL;
+	sd_event *loop = NULL;
+	sd_bus_slot *slot = NULL;
+	sd_bus_error error = SD_BUS_ERROR_NULL;
+	char *service = NULL;
+	int r, dest = -1, state;
+	char match[LEN];
+
+	if(argc < 3) {
+		fprintf(stderr, usage, argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if(!strcmp(argv[2], "on"))
+		dest = 1;
+	if(!strcmp(argv[2], "off"))
+		dest = 0;
+
+	if(dest != 0 && dest != 1) {
+		fprintf(stderr, usage, argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	r = sd_bus_default_system(&conn);
+	if(r < 0) {
+		fprintf(stderr, "Error connecting to system bus: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+	r = mapper_get_service(conn, argv[1], &service);
+	if (r < 0) {
+		fprintf(stderr, "Error obtaining host service: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+	r = sd_event_default(&loop);
+	if (r < 0) {
+		fprintf(stderr, "Error obtaining event loop: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+	r = sd_bus_attach_event(conn, loop, SD_EVENT_PRIORITY_NORMAL);
+	if (r < 0) {
+		fprintf(stderr, "Failed to attach system "
+				"bus to event loop: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+	if(strlen(matchfmt) + strnlen(argv[1], LEN) > LEN) {
+		r = -E2BIG;
+		fprintf(stderr, "Error adding match rule: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+	sprintf(match, matchfmt, argv[1], service);
+
+	r = sd_bus_add_match(conn,
+			&slot,
+			match,
+			callback,
+			loop);
+	if(r < 0) {
+		fprintf(stderr, "Error adding match rule: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+	r = sd_bus_get_property_trivial(conn,
+			service,
+			argv[1],
+			"org.openbmc.control.Power",
+			"pgood",
+			&error,
+			'i',
+			&state);
+	if(r < 0) {
+		fprintf(stderr, "Error getting property: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+	if(dest == state)
+		goto finish;
+
+	r = sd_event_loop(loop);
+	if(r < 0) {
+		fprintf(stderr, "Error starting event loop: %s\n",
+				strerror(-r));
+		goto finish;
+	}
+
+finish:
+	exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}