+static bool ga_open_pidfile(const char *pidfile)
+{
+ int pidfd;
+ char pidstr[32];
+
+ pidfd = open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (pidfd == -1 || lockf(pidfd, F_TLOCK, 0)) {
+ g_critical("Cannot lock pid file, %s", strerror(errno));
+ if (pidfd != -1) {
+ close(pidfd);
+ }
+ return false;
+ }
+
+ if (ftruncate(pidfd, 0) || lseek(pidfd, 0, SEEK_SET)) {
+ g_critical("Failed to truncate pid file");
+ goto fail;
+ }
+ snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
+ if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
+ g_critical("Failed to write pid file");
+ goto fail;
+ }
+
+ return true;
+
+fail:
+ unlink(pidfile);
+ return false;
+}
+#else /* _WIN32 */
+static bool ga_open_pidfile(const char *pidfile)
+{
+ return true;
+}
+#endif
+
+static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
+{
+ return strcmp(str1, str2);
+}
+
+/* disable commands that aren't safe for fsfreeze */
+static void ga_disable_non_whitelisted(void)
+{
+ char **list_head, **list;
+ bool whitelisted;
+ int i;
+
+ list_head = list = qmp_get_command_list();
+ while (*list != NULL) {
+ whitelisted = false;
+ i = 0;
+ while (ga_freeze_whitelist[i] != NULL) {
+ if (strcmp(*list, ga_freeze_whitelist[i]) == 0) {
+ whitelisted = true;
+ }
+ i++;
+ }
+ if (!whitelisted) {
+ g_debug("disabling command: %s", *list);
+ qmp_disable_command(*list);
+ }
+ g_free(*list);
+ list++;
+ }
+ g_free(list_head);
+}
+
+/* [re-]enable all commands, except those explicitly blacklisted by user */
+static void ga_enable_non_blacklisted(GList *blacklist)
+{
+ char **list_head, **list;
+
+ list_head = list = qmp_get_command_list();
+ while (*list != NULL) {
+ if (g_list_find_custom(blacklist, *list, ga_strcmp) == NULL &&
+ !qmp_command_is_enabled(*list)) {
+ g_debug("enabling command: %s", *list);
+ qmp_enable_command(*list);
+ }
+ g_free(*list);
+ list++;
+ }
+ g_free(list_head);
+}
+
+static bool ga_create_file(const char *path)
+{
+ int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR);
+ if (fd == -1) {
+ g_warning("unable to open/create file %s: %s", path, strerror(errno));
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static bool ga_delete_file(const char *path)
+{
+ int ret = unlink(path);
+ if (ret == -1) {
+ g_warning("unable to delete file: %s: %s", path, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool ga_is_frozen(GAState *s)
+{
+ return s->frozen;
+}
+
+void ga_set_frozen(GAState *s)
+{
+ if (ga_is_frozen(s)) {
+ return;
+ }
+ /* disable all non-whitelisted (for frozen state) commands */
+ ga_disable_non_whitelisted();
+ g_warning("disabling logging due to filesystem freeze");
+ ga_disable_logging(s);
+ s->frozen = true;
+ if (!ga_create_file(s->state_filepath_isfrozen)) {
+ g_warning("unable to create %s, fsfreeze may not function properly",
+ s->state_filepath_isfrozen);
+ }
+}
+
+void ga_unset_frozen(GAState *s)
+{
+ if (!ga_is_frozen(s)) {
+ return;
+ }
+
+ /* if we delayed creation/opening of pid/log files due to being
+ * in a frozen state at start up, do it now
+ */
+ if (s->deferred_options.log_filepath) {
+ s->log_file = fopen(s->deferred_options.log_filepath, "a");
+ if (!s->log_file) {
+ s->log_file = stderr;
+ }
+ s->deferred_options.log_filepath = NULL;
+ }
+ ga_enable_logging(s);
+ g_warning("logging re-enabled due to filesystem unfreeze");
+ if (s->deferred_options.pid_filepath) {
+ if (!ga_open_pidfile(s->deferred_options.pid_filepath)) {
+ g_warning("failed to create/open pid file");
+ }
+ s->deferred_options.pid_filepath = NULL;
+ }
+
+ /* enable all disabled, non-blacklisted commands */
+ ga_enable_non_blacklisted(s->blacklist);
+ s->frozen = false;
+ if (!ga_delete_file(s->state_filepath_isfrozen)) {
+ g_warning("unable to delete %s, fsfreeze may not function properly",
+ s->state_filepath_isfrozen);
+ }
+}
+