ukui-control-center/changeUserPwd/run-passwd.cpp

809 lines
30 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* qt会将glib里的signals成员识别为宏所以取消该宏
* 后面如果用到signals时使用Q_SIGNALS代替即可
**/
#ifdef signals
#undef signals
#endif
#include <glib.h>
#include <gio/gio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdio.h>
#include "run-passwd.h"
/* Buffer size for backend output */
#define BUFSIZE 1024
/* Passwd states */
//后端passwd的状态NONE应该是passwd还没有启动ERROR表示报错但还没退出
typedef enum {
PASSWD_STATE_NONE, /* Passwd is not asking for anything */
PASSWD_STATE_AUTH, /* Passwd is asking for our current password */
PASSWD_STATE_NEW, /* Passwd is asking for our new password */
PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */
PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */
} PasswdState;
struct PasswdHandler {
// GtkBuilder *ui;
const char *current_password;
const char *new_password;
const char *retyped_password;
/* Communication with the passwd program */
GPid backend_pid;
GIOChannel *backend_stdin;
GIOChannel *backend_stdout;
GIOChannel *backend_stderr;
GQueue *backend_stdin_queue; /* Write queue to backend_stdin */
/* GMainLoop IDs */
guint backend_child_watch_id; /* g_child_watch_add (PID) */
guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */
/* State of the passwd program */
PasswdState backend_state;
gboolean changing_password;
PasswdCallback auth_cb;
gpointer auth_cb_data;
PasswdCallback chpasswd_cb;
gpointer chpasswd_cb_data;
};
//GQuark是一个guint32
static GQuark
passwd_error_quark (void)
{
static GQuark q = 0;
//返回错误的标识码
if (q == 0) {
q = g_quark_from_static_string("passwd_error");
}
return q;
}
/* Error handling */
#define PASSWD_ERROR (passwd_error_quark ())
static void stop_passwd (PasswdHandler *passwd_handler);
static void free_passwd_resources (PasswdHandler *passwd_handler);
static gboolean io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler);
static gboolean io_watch_stdout2 (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler);
static void free_passwd_resources (PasswdHandler *passwd_handler)
{
GError *error = NULL;
/* Remove the child watcher */
if (passwd_handler->backend_child_watch_id != 0) {
g_source_remove (passwd_handler->backend_child_watch_id);
passwd_handler->backend_child_watch_id = 0;
}
/* Close IO channels (internal file descriptors are automatically closed) */
if (passwd_handler->backend_stdin != NULL) {
if (g_io_channel_shutdown (passwd_handler->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) {
g_warning ("Could not shutdown backend_stdin IO channel: %s", error->message);
g_error_free (error);
error = NULL;
}
g_io_channel_unref (passwd_handler->backend_stdin);
passwd_handler->backend_stdin = NULL;
}
if (passwd_handler->backend_stdout != NULL) {
if (g_io_channel_shutdown (passwd_handler->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) {
g_warning ("Could not shutdown backend_stdout IO channel: %s", error->message);
g_error_free (error);
error = NULL;
}
g_io_channel_unref (passwd_handler->backend_stdout);
passwd_handler->backend_stdout = NULL;
}
/* Remove IO watcher */
if (passwd_handler->backend_stdout_watch_id != 0) {
g_source_remove (passwd_handler->backend_stdout_watch_id);
passwd_handler->backend_stdout_watch_id = 0;
}
/* Close PID */
//因为flag为G_SPAWN_DO_NOT_REAP_CHILD,所以child不会自动的被reap掉需要在子进程上free
if (passwd_handler->backend_pid != -1) {
g_spawn_close_pid (passwd_handler->backend_pid);
passwd_handler->backend_pid = -1;
}
/* Clear backend state */
passwd_handler->backend_state = PASSWD_STATE_NONE;
}
static void authenticate (PasswdHandler *passwd_handler)
{
gchar *s;
s = g_strdup_printf ("%s\n", passwd_handler->current_password);
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
}
static void io_queue_pop (GQueue *queue, GIOChannel *channel)
{
gchar *buf;
gsize bytes_written;
GError *error = NULL;
buf = (gchar *)g_queue_pop_head (queue);
if (buf != NULL) {
//将队列中的首元素写入到channel中
if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) {
g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message);
g_error_free (error);
}
/* Ensure passwords are cleared from memory */
//清除内存中的passwords
memset (buf, 0, strlen (buf));
g_free (buf);
}
}
static gboolean is_string_complete (gchar *str, ...)
{
va_list ap;
gchar *arg;
if (strlen (str) == 0) {
return FALSE;
}
va_start (ap, str);
while ((arg = va_arg (ap, char *)) != NULL) {
if (g_strrstr (str, arg) != NULL) {
va_end (ap);
return TRUE;
}
}
va_end (ap);
return FALSE;
}
int isDomainUser(const char* username)
{
FILE *fp;
fp=fopen("/etc/passwd","r");
if(fp == NULL)
{
return 1;
}
char buf[1024], name[128];
while(!feof(fp))
{
if(fgets(buf,sizeof (buf),fp) == NULL)
{
break;
}
sscanf(buf,"%[^:]",name);
if(strcmp(name,username) == 0)
{
fclose(fp);
return 0;
}
}
fclose(fp);
return 1;
}
static gboolean io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler)
{
static GString *str = NULL; /* Persistent buffer */
gchar buf[BUFSIZE]; /* Temporary buffer */
gsize bytes_read;
GError *gio_error = NULL; /* Error returned by functions */
GError *error = NULL; /* Error sent to callbacks */
//GtkBuilder *dialog;
gboolean reinit = FALSE;
/* Initialize buffer */
if (str == NULL) {
str = g_string_new ("");
}
//dialog = passwd_handler->ui;
//buf将保存从channel中读取到的数据,bytes_read表示从buf中读取的数据长度
if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &gio_error)
!= G_IO_STATUS_NORMAL) {
g_warning ("IO Channel read error: %s", gio_error->message);
g_error_free (gio_error);
return TRUE;
}
str = g_string_append_len (str, buf, bytes_read);
g_warning("----io_watch_stdout DEBUG#msg: %s\n", str->str);
g_warning("----io_watch_stdout DEBUG#0msg: %d\n", passwd_handler->backend_state);
/* In which state is the backend? */
switch (passwd_handler->backend_state) {
case PASSWD_STATE_AUTH:
/* Passwd is asking for our current password */
if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", "failed", NULL)) {
if (g_strrstr (str->str, "New password: ") != NULL || g_strrstr (str->str, "New Password: ") != NULL) {
/* Authentication successful */
passwd_handler->backend_state = PASSWD_STATE_NEW;
/* Trigger callback to update authentication status */
if (passwd_handler->auth_cb)
passwd_handler->auth_cb (passwd_handler,
NULL,
passwd_handler->auth_cb_data);
} else {
/* Authentication failed */
// 域用户出错只关注服务器消息stdoutsdterr消息不关注
if (isDomainUser(g_get_user_name())) {
break;
}
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
"Authentication token manipulation error!");
g_warning ("Change password failed: %s", error->message);
passwd_handler->changing_password = FALSE;
/* This error can happen both while authenticating or while changing password:
* if chpasswd_cb is set, this means we're already changing password */
if (passwd_handler->chpasswd_cb)
passwd_handler->chpasswd_cb (passwd_handler,
error,
passwd_handler->auth_cb_data);
else if (passwd_handler->auth_cb)
passwd_handler->auth_cb (passwd_handler,
error,
passwd_handler->auth_cb_data);
g_error_free (error);
}
reinit = TRUE;
}
break;
case PASSWD_STATE_NEW:
/* Passwd is asking for our new password */
if (is_string_complete (str->str, "assword: ", NULL)) {
/* Advance to next state */
passwd_handler->backend_state = PASSWD_STATE_RETYPE;
/* Pop retyped password from queue and into IO channel */
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
reinit = TRUE;
}
break;
case PASSWD_STATE_RETYPE:
/* Passwd is asking for our retyped new password */
// if (is_string_complete (str->str,
// "successfully",
// "short",
// "longer",
// "palindrome",
// "dictionary",
// "simple",
// "simplistic",
// "similar",
// "different",
// "case",
// "wrapped",
// "recovered",
// "recent",
// "unchanged",
// "match",
// "1 numeric or special",
// "failure",
// "length",
// NULL)) {
if (TRUE){
if (g_strrstr (str->str, "successfully") != NULL) {
/* Hooray! */
/* Trigger callback to update status */
if (passwd_handler->chpasswd_cb)
passwd_handler->chpasswd_cb (passwd_handler,
NULL,
passwd_handler->chpasswd_cb_data);
} else {
/* Ohnoes! */
// 域用户出错只关注服务器消息stdoutsdterr消息不关注
if (isDomainUser(g_get_user_name())) {
break;
}
if (g_strrstr (str->str, "recovered") != NULL) {
/* What does this indicate?
* "Authentication information cannot be recovered?" from libpam? */
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
str->str);
}/* else if (g_strrstr (str->str, "short") != NULL ||
g_strrstr (str->str, "longer") != NULL) {
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
"New password length is too short!");
} else if (g_strrstr (str->str, "palindrome") != NULL ||
g_strrstr (str->str, "simple") != NULL ||
g_strrstr (str->str, "simplistic") != NULL ||
g_strrstr (str->str, "dictionary") != NULL) {
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
"The new password is too simple!");
} else if (g_strrstr (str->str, "similar") != NULL ||
g_strrstr (str->str, "different") != NULL ||
g_strrstr (str->str, "case") != NULL ||
g_strrstr (str->str, "wrapped") != NULL) {
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
"The new password is too similar to the old one!");
} else if (g_strrstr (str->str, "1 numeric or special") != NULL) {
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
"The new password must contain numbers or special characters!");
} else if (g_strrstr (str->str, "unchanged") != NULL ||
g_strrstr (str->str, "match") != NULL) {
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
"The new password is the same as the old one!");
} else if (g_strrstr (str->str, "recent") != NULL) {
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
"The new password has been used recently!");
} else if (g_strrstr (str->str, "failure") != NULL) {
//Authentication failure
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
"Your password has been changed after you verify!");
} */else {
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
str->str);
}
/* At this point, passwd might have exited, in which case
* child_watch_cb should clean up for us and remove this watcher.
* On some error conditions though, passwd just re-prompts us
* for our new password. */
passwd_handler->backend_state = PASSWD_STATE_ERR;
passwd_handler->changing_password = FALSE;
/* Trigger callback to update status */
if (passwd_handler->chpasswd_cb)
passwd_handler->chpasswd_cb (passwd_handler,
error,
passwd_handler->chpasswd_cb_data);
g_error_free (error);
}
reinit = TRUE;
/* child_watch_cb should clean up for us now */
}
break;
case PASSWD_STATE_NONE:
/* Passwd is not asking for anything yet */
if (is_string_complete (str->str, "assword: ", NULL)) {
/* If the user does not have a password set,
* passwd will immediately ask for the new password,
* so skip the AUTH phase */
if (is_string_complete (str->str, "new", "New", NULL)) {
gchar *pw;
passwd_handler->backend_state = PASSWD_STATE_NEW;
/* since passwd didn't ask for our old password
* in this case, simply remove it from the queue */
pw = (gchar *)g_queue_pop_head (passwd_handler->backend_stdin_queue);
g_free (pw);
/* Pop the IO queue, i.e. send new password */
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
} else {
passwd_handler->backend_state = PASSWD_STATE_AUTH;
/* Pop the IO queue, i.e. send current password */
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
}
reinit = TRUE;
}
break;
default:
/* Passwd has returned an error */
reinit = TRUE;
break;
}
if (reinit) {
g_string_free (str, TRUE);
str = NULL;
}
/* Continue calling us */
return TRUE;
}
static gboolean io_watch_stdout2 (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler)
{
g_warning("----io_watch_stdout2 revieve server message");
static GString *str = NULL; /* Persistent buffer */
gchar buf[BUFSIZE]; /* Temporary buffer */
gsize bytes_read;
GError *gio_error = NULL; /* Error returned by functions */
GError *error = NULL; /* Error sent to callbacks */
//GtkBuilder *dialog;
gboolean reinit = FALSE;
/* Initialize buffer */
if (str == NULL) {
str = g_string_new ("");
}
//dialog = passwd_handler->ui;
//buf将保存从channel中读取到的数据,bytes_read表示从buf中读取的数据长度
if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &gio_error)
!= G_IO_STATUS_NORMAL) {
g_warning ("IO Channel read error: %s", gio_error->message);
g_error_free (gio_error);
return TRUE;
}
str = g_string_append_len (str, buf, bytes_read);
g_warning("----io_watch_stdout2 DEBUG#msg2: %s\n", str->str);
if (is_string_complete (str->str, "Server message", "System is offline", NULL)) {
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
str->str);
g_warning ("Change password failed: %s", error->message);
passwd_handler->changing_password = FALSE;
/* This error can happen both while authenticating or while changing password:
* if chpasswd_cb is set, this means we're already changing password */
if (passwd_handler->chpasswd_cb)
passwd_handler->chpasswd_cb (passwd_handler,
error,
passwd_handler->auth_cb_data);
else if (passwd_handler->auth_cb)
passwd_handler->auth_cb (passwd_handler,
error,
passwd_handler->auth_cb_data);
g_error_free (error);
}
return TRUE;
}
/* Child watcher */
static void child_watch_cb (GPid pid, gint status, PasswdHandler *passwd_handler)
{
//子进程正常结束为非0
if (WIFEXITED (status)) {
//取得子进程正常退出时返回的结束代码
if (WEXITSTATUS (status) >= 255) {
g_warning ("Child exited unexpectedly");
}
}
free_passwd_resources (passwd_handler);
}
static void stop_passwd (PasswdHandler *passwd_handler)
{
/* This is the standard way of returning from the dialog with passwd.
* If we return this way we can safely kill passwd as it has completed
* its task.
*/
if (passwd_handler->backend_pid != -1) {
kill (passwd_handler->backend_pid, 9);
}
/* We must run free_passwd_resources here and not let our child
* watcher do it, since it will access invalid memory after the
* dialog has been closed and cleaned up.
*
* If we had more than a single thread we'd need to remove
* the child watch before trying to kill the child.
*/
free_passwd_resources (passwd_handler);
}
static gboolean spawn_passwd (PasswdHandler *passwd_handler, GError **error)
{
gchar *argv[2];
gchar *envp[1];
gint my_stdin, my_stdout, my_stderr;
argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */
argv[1] = NULL;
envp[0] = NULL; /* If we pass an empty array as the environment,
* will the childs environment be empty, and the
* locales set to the C default? From the manual:
* "If envp is NULL, the child inherits its
* parent'senvironment."
* If I'm wrong here, we somehow have to set
* the locales here.
*/
//创建一个管道,进行通信,子进程执行passwd命令
if (!g_spawn_async_with_pipes (NULL, /* Working directory */
argv, /* Argument vector */
envp, /* Environment */
G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */
NULL, /* Child setup 在子进程调用exec()之前,该函数会被调用)*/
NULL, /* Data to child setup */
&passwd_handler->backend_pid, /* PID */
&my_stdin, /* Stdin */
&my_stdout, /* Stdout */
&my_stderr, /* Stderr */
error)) { /* GError */
/* An error occured */
free_passwd_resources (passwd_handler);
return FALSE;
}
/* 2>&1 */
//复制文件描述符也就是将stderr重定向到stdout
// if (dup2 (my_stderr, my_stdout) == -1) {
// /* Failed! */
// g_set_error_literal (error,
// PASSWD_ERROR,
// PASSWD_ERROR_BACKEND,
// strerror (errno));
// /* Clean up */
// stop_passwd (passwd_handler);
// return FALSE;
// }
/* Open IO Channels */
//指定一个文件描述符创建一个IO Channel默认使用UTF-8编码格式
passwd_handler->backend_stdin = g_io_channel_unix_new (my_stdin);
passwd_handler->backend_stdout = g_io_channel_unix_new (my_stdout);
passwd_handler->backend_stderr = g_io_channel_unix_new(my_stderr);
/* Set raw encoding */
/* Set nonblocking mode */
//设置通道的编码方式为NULL,设置为非阻塞的方式
if (g_io_channel_set_encoding (passwd_handler->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
g_io_channel_set_encoding (passwd_handler->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
g_io_channel_set_encoding (passwd_handler->backend_stderr, NULL, error) != G_IO_STATUS_NORMAL ||
g_io_channel_set_flags (passwd_handler->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
g_io_channel_set_flags (passwd_handler->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
g_io_channel_set_flags (passwd_handler->backend_stderr, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {
/* Clean up */
stop_passwd (passwd_handler);
return FALSE;
}
/* Turn off buffering */
//只有通道的编码方式为NULL才能设置缓冲状态为FASLE其他任何编码通道必须被缓冲这里是为了清掉上次的密码
g_io_channel_set_buffered (passwd_handler->backend_stdin, FALSE);
g_io_channel_set_buffered (passwd_handler->backend_stdout, FALSE);
g_io_channel_set_buffered (passwd_handler->backend_stderr, FALSE);
/* Add IO Channel watcher */
passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stdout,
G_IO_IN,
(GIOFunc) io_watch_stdout2, passwd_handler);
//当IO通道的状态为G_IO_IN(从IO通道读数据时)或者G_IO_PRI(读紧急数据时)时调用io_watch_stdout
passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stderr,
G_IO_IN /*| G_IO_PRI*/ ,
(GIOFunc) io_watch_stdout, passwd_handler);
/* Add child watcher */
//在指定pid的进程退出时调用child_watch_cb(),进行错误检查,以及资源回收
passwd_handler->backend_child_watch_id = g_child_watch_add (passwd_handler->backend_pid, (GChildWatchFunc) child_watch_cb, passwd_handler);
/* Success! */
return TRUE;
}
static void update_password (PasswdHandler *passwd_handler)
{
gchar *s;
s = g_strdup_printf ("%s\n", passwd_handler->new_password);
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
/* We need to allocate new space because io_queue_pop() g_free()s
* every element of the queue after it's done */
g_queue_push_tail (passwd_handler->backend_stdin_queue, g_strdup (s));
}
gboolean passwd_change_password (PasswdHandler *passwd_handler,
const char *new_password,
PasswdCallback cb,
const gpointer user_data)
{
GError *error = NULL;
passwd_handler->changing_password = TRUE;
passwd_handler->new_password = new_password;
passwd_handler->chpasswd_cb = cb;
passwd_handler->chpasswd_cb_data = user_data;
/* Stop passwd if an error occured and it is still running */
if (passwd_handler->backend_state == PASSWD_STATE_ERR) {
/* Stop passwd, free resources */
stop_passwd (passwd_handler);
}
/* Check that the backend is still running, or that an error
* has occured but it has not yet exited */
if (passwd_handler->backend_pid == -1) {
/* If it is not, re-run authentication */
/* Spawn backend */
stop_passwd (passwd_handler);
if (!spawn_passwd (passwd_handler, &error)) {
g_error_free (error);
return FALSE;
}
/* Add current and new passwords to queue */
//将当前的密码和新密码入队,新密码会入队两次
authenticate (passwd_handler);
update_password (passwd_handler);
} else {
/* Only add new passwords to queue */
update_password (passwd_handler);
}
/* Pop new password through the backend. If user has no password, popping the queue
would output current password, while 'passwd' is waiting for the new one. So wait
for io_watch_stdout() to remove current password from the queue, and output
the new one for us.*/
//如果密码为空将新进队列的密码作为current_passwd弹出
if (passwd_handler->current_password)
{
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
}
/* Our IO watcher should now handle the rest */
return TRUE;
}
void passwd_authenticate (PasswdHandler *passwd_handler,
const char *current_password,
PasswdCallback cb,
const gpointer user_data)
{
GError *error = NULL;
/* Don't stop if we've already started chaging password */
if (passwd_handler->changing_password)
return;
/* Clear data from possible previous attempts to change password */
passwd_handler->new_password = NULL;
passwd_handler->chpasswd_cb = NULL;
passwd_handler->chpasswd_cb_data = NULL;
g_queue_foreach (passwd_handler->backend_stdin_queue, (GFunc) g_free, NULL);
g_queue_clear (passwd_handler->backend_stdin_queue);
passwd_handler->current_password = current_password;
passwd_handler->auth_cb = cb;
passwd_handler->auth_cb_data = user_data;
/* Spawn backend */
//重新启动后台passwd
stop_passwd (passwd_handler);
if (!spawn_passwd (passwd_handler, &error)) {
g_warning ("%s", error->message);
g_error_free (error);
return;
}
//将current passwd从尾部插入队列
authenticate (passwd_handler);
/* Our IO watcher should now handle the rest */
}
PasswdHandler * passwd_init ()
{
PasswdHandler *passwd_handler;
passwd_handler = g_new0 (PasswdHandler, 1);
/* Initialize backend_pid. -1 means the backend is not running */
//-1代表后台还没启动
passwd_handler->backend_pid = -1;
/* Initialize IO Channels */
passwd_handler->backend_stdin = NULL;
passwd_handler->backend_stdout = NULL;
/* Initialize write queue */
passwd_handler->backend_stdin_queue = g_queue_new ();
/* Initialize watchers */
passwd_handler->backend_child_watch_id = 0;
passwd_handler->backend_stdout_watch_id = 0;
/* Initialize backend state */
passwd_handler->backend_state = PASSWD_STATE_NONE;
passwd_handler->changing_password = FALSE;
return passwd_handler;
}
void passwd_destroy (PasswdHandler *passwd_handler)
{
g_queue_free (passwd_handler->backend_stdin_queue);
stop_passwd (passwd_handler);
g_free (passwd_handler);
}