2 * Copyright (c) 2014, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include <sys/types.h>
40 #include <murphy/common.h>
41 #include <murphy/common/process.h>
42 #include <murphy/core/lua-bindings/murphy.h>
43 #include <murphy/core/lua-utils/error.h>
44 #include <murphy/core/lua-utils/object.h>
45 #include <murphy/core/lua-utils/funcbridge.h>
47 #include <murphy-db/mqi.h>
48 #include <murphy-db/mql.h>
53 #include <libxml/parser.h>
54 #include <libxml/tree.h>
61 #define USER_MANAGER_CLASS MRP_LUA_CLASS(user_manager, lua)
63 /* TODO: associate the two structs with each other */
72 char *defaultapps_path;
75 mrp_list_hook_t users;
81 } user_manager_config_t;
88 char *runningapp_path;
90 user_manager_config_t *ctx;
94 /* TODO: move current_user to ctx */
95 static user_config_t *current_user;
97 /* TODO: move users to ctx */
98 static mrp_list_hook_t users;
100 static int user_manager_create(lua_State *L);
101 static void user_manager_destroy(void *ctx);
103 static int lua_set_lastinfo(lua_State *L);
104 static int lua_get_lastinfo(lua_State *L);
105 static int lua_get_userlist(lua_State *L);
106 static int lua_change_user(lua_State *L);
109 static void hs_check(mrp_timer_t *t, void *user_data);
112 MRP_LUA_METHOD_LIST_TABLE(user_manager_methods,
113 MRP_LUA_METHOD_CONSTRUCTOR(user_manager_create)
114 MRP_LUA_METHOD(getUserList, lua_get_userlist)
115 MRP_LUA_METHOD(setLastinfo, lua_set_lastinfo)
116 MRP_LUA_METHOD(getLastinfo, lua_get_lastinfo)
117 MRP_LUA_METHOD(changeUser, lua_change_user));
119 MRP_LUA_METHOD_LIST_TABLE(user_manager_overrides,
120 MRP_LUA_OVERRIDE_CALL (user_manager_create));
122 MRP_LUA_DEFINE_CLASS(user_manager, lua, user_manager_t, user_manager_destroy,
123 user_manager_methods, user_manager_overrides, NULL, NULL, NULL, NULL, NULL,
124 MRP_LUA_CLASS_EXTENSIBLE);
126 static user_manager_t *user_manager_check(lua_State *L, int idx)
128 return (user_manager_t *) mrp_lua_check_object(L, USER_MANAGER_CLASS, idx);
131 static void user_manager_destroy(void *ctx)
134 mrp_log_info("user_manager_destroy");
137 static int user_manager_create(lua_State *L)
139 user_manager_t *ctx = NULL;
141 ctx = (user_manager_t *) mrp_lua_create_object(L, USER_MANAGER_CLASS, NULL,
144 mrp_lua_push_object(L, ctx);
149 static bool mkdir_if_needed(const char *dir)
151 mrp_debug("creating directory '%s'", dir);
153 if (mkdir(dir, 0700) == -1) {
156 if (errno != EEXIST) {
157 mrp_log_error("failed to create directory '%s'", dir);
161 /* there already is a directory or a file of that name */
163 if (stat(dir, &buf) == -1) {
164 mrp_log_error("failed to stat directory '%s'", dir);
168 if (!S_ISDIR(buf.st_mode)) {
169 mrp_log_error("file '%s' exists and isn't a directory", dir);
180 static bool verify_appid(const char *appid)
182 /* check if appid contains illegal elements, since it's being used in
185 /* allowed are [a-zA-Z0-9.-_] TODO: might be faster to do this with a
199 for (i = 0; i < len; i++) {
202 if (c >= 'a' && c <= 'z')
205 if (c >= 'A' && c <= 'Z')
208 if (c >= '0' && c <= '9')
211 if (c == '.' || c == '-' || c == '_')
220 /* lua_get_userlist returns a list of users in the system. */
222 static int lua_get_userlist(lua_State *L)
224 mrp_list_hook_t *p, *n;
225 user_config_t *config;
229 narg = lua_gettop(L);
232 return luaL_error(L, "expecting no arguments");
234 mgr = user_manager_check(L, 1);
237 return luaL_error(L, "no self pointer");
239 /* make a user table and push it to the stack */
243 /* push all user strings to a table */
245 mrp_list_foreach(&users, p, n) {
246 config = mrp_list_entry(p, typeof(*config), hook);
248 mrp_debug("read user '%s'", config->name);
250 lua_pushinteger(L, i++);
251 lua_pushstring(L, config->name);
255 /* push current user to stack */
258 lua_pushstring(L, current_user->name);
266 static int terminate_app(const aul_app_info *ai, void *user_data)
270 MRP_UNUSED(user_data);
275 mrp_log_info("terminate %s", ai->appid ? ai->appid : "unknown application");
277 /* org.tizen.ico.homescreen, org.tizen.ico.statusbar, others */
279 ret = aul_terminate_pid(ai->pid);
280 mrp_log_info("termination %s",
281 ret < 0 ? "not successful" : "successful");
287 static int iter_aul_app_info(const aul_app_info *ai, void *user_data)
289 pid_t *hs_pid = (pid_t *) user_data;
291 MRP_UNUSED(user_data);
293 if (!ai || !ai->appid)
296 mrp_log_info("ai: pid %i, appid: %s, pkg_name %s", ai->pid, ai->appid,
297 ai->pkg_name ? ai->pkg_name : "NULL");
299 if (current_user && strcmp(current_user->homescreen, ai->appid) == 0) {
306 static pid_t get_hs_pid(const char *homescreen)
310 if (!aul_app_is_running(homescreen))
313 aul_app_get_running_app_info(iter_aul_app_info, &hs_pid);
319 static bool launch_hs(user_manager_config_t *ctx)
321 bundle *b = bundle_create();
324 mrp_log_error("could not create bundle");
333 bundle_add(b, "HS_PARAM_U", current_user->user_dir);
334 bundle_add(b, "HS_PARAM_D", current_user->runningapp_path);
335 bundle_add(b, "HS_PARAM_DD", ctx->defaultapps_path);
336 bundle_add(b, "HS_PARAM_FLG", ctx->flag_file_path);
338 mrp_log_info("launching new homescreen for %s, parameters: %s %s %s %s",
339 current_user->name, current_user->user_dir,
340 current_user->runningapp_path, ctx->defaultapps_path,
341 ctx->flag_file_path);
343 ctx->current_hs_pid = aul_launch_app(current_user->homescreen, b);
350 static void create_flag_file(user_manager_config_t *ctx)
352 FILE *f = fopen(ctx->flag_file_path, "w");
357 static void delete_flag_file(user_manager_config_t *ctx)
359 unlink(ctx->flag_file_path);
362 static void launch_hs_deferred(mrp_timer_t *t, void *user_data)
364 user_manager_config_t *ctx = (user_manager_config_t *) user_data;
370 delete_flag_file(ctx);
375 /* change_user authenticates the user, destroys the current session and starts
376 * a new session with the new user. */
378 static bool change_user(const char *user, const char *passwd)
380 mrp_list_hook_t *p, *n;
381 user_config_t *config;
382 user_manager_config_t *ctx;
384 if (strcmp(user, "") == 0 && strcmp(passwd, "") == 0) {
385 /* this is a logout */
388 mrp_log_error("trying to log out a non-existing user");
392 ctx = current_user->ctx;
396 /* get the current homescreen pid */
398 if (ctx->current_hs_pid > 0) {
399 /* terminate the current homescreen and all other applications */
400 mrp_log_info("terminating homescreen %i", ctx->current_hs_pid);
401 aul_app_get_running_app_info(terminate_app, ctx);
402 ctx->current_hs_pid = -1;
408 mrp_list_foreach(&users, p, n) {
409 config = mrp_list_entry(p, typeof(*config), hook);
413 if (strcmp(user, config->name) == 0 &&
414 strcmp(passwd, config->passwd) == 0) {
418 /* "authenticated" now */
419 mrp_log_info("authenticated user %s", user);
421 if (current_user && strcmp(user, current_user->name) == 0) {
422 mrp_log_warning("user '%s' is already logged in", user);
428 create_flag_file(ctx);
430 /* TODO: save current state meaning running programs */
432 /* change the current user */
434 current_user = config;
436 /* get the current homescreen pid */
438 if (ctx->current_hs_pid > 0) {
439 /* terminate the current homescreen and other applications */
440 aul_app_get_running_app_info(terminate_app, ctx);
441 ctx->current_hs_pid = -1;
444 /* launch new homescreen, wait the system controller amount :-) */
446 /* TODO: should be done from dead_signal handler, but that doesn't
447 appear to be working properly */
449 t = mrp_add_timer(ctx->ml, 2000, launch_hs_deferred, ctx);
460 static int lua_change_user(lua_State *L)
467 narg = lua_gettop(L);
470 return luaL_error(L, "expecting two arguments");
472 ctx = user_manager_check(L, 1);
475 return luaL_error(L, "no self pointer");
477 if (!lua_isstring(L, -1) || !lua_isstring(L, -2))
478 return luaL_error(L, "argument error");
480 user = lua_tostring(L, -2);
483 return luaL_error(L, "string error");
485 passwd = lua_tostring(L, -1);
488 return luaL_error(L, "string error");
490 mrp_log_info("lua_change_user '%s'", user);
492 if (change_user(user, passwd))
493 lua_pushboolean(L, 1);
495 lua_pushboolean(L, 0);
500 /* set_lastinfo stores the lastinfo string to a file, whose name
501 is derived from string appid. */
503 static bool set_lastinfo(lua_State *L, const char *appid, const char *lastinfo,
507 int appid_len = strlen(appid);
513 if (!verify_appid(appid))
519 base = current_user->user_dir;
524 base_len = strlen(base);
526 if (base_len + 1 + appid_len >= 256)
530 path[base_len] = '/';
531 strcpy(path+base_len+1, appid);
532 path[base_len + 1 + appid_len] = '\0';
534 mrp_log_info("writing lastinfo to '%s'\n", path);
536 fd = open(path, O_WRONLY | O_CREAT, 0600);
539 mrp_log_error("failed to open lastinfo file for writing");
543 if (write(fd, lastinfo, lastinfo_len) < 0) {
544 mrp_log_error("failed to write to lastinfo file");
554 static int lua_set_lastinfo(lua_State *L)
557 const char *lastinfo;
562 narg = lua_gettop(L);
565 return luaL_error(L, "expecting two arguments");
567 ctx = user_manager_check(L, 1);
570 return luaL_error(L, "no self pointer");
572 if (!lua_isstring(L, -1) || !lua_isstring(L, -2))
575 lastinfo = lua_tostring(L, -2);
580 lastinfo_len = lua_strlen(L, -2);
582 appid = lua_tostring(L, -1);
587 mrp_log_info("set_lastinfo appid: '%s', lastinfo: '%s'", appid, lastinfo);
589 if (!set_lastinfo(L, appid, lastinfo, lastinfo_len))
595 return luaL_error(L, "set_lastinfo error");}
597 /* get_lastinfo retrieves the lastinfo string from a file, whose
598 name is derived from string appid. */
600 static bool get_lastinfo(lua_State *L, const char *appid)
605 int appid_len = strlen(appid);
606 int fd, ret, base_len;
611 if (!verify_appid(appid))
617 base = current_user->user_dir;
622 base_len = strlen(base);
624 if (base_len + 1 + appid_len >= 256)
628 path[base_len] = '/';
629 strcpy(path+base_len+1, appid);
630 path[base_len + 1 + appid_len] = '\0';
632 mrp_log_info("reading lastinfo from '%s'\n", path);
634 fd = open(path, O_RDONLY);
637 mrp_log_warning("failed to read lastinfo from '%s'", path);
641 ret = read(fd, result, sizeof(result));
643 if (ret >= 0 && ret != 2048) {
645 lua_pushstring(L, result);
653 static int lua_get_lastinfo(lua_State *L)
659 narg = lua_gettop(L);
662 return luaL_error(L, "expecting one argument");
664 ctx = user_manager_check(L, 1);
667 return luaL_error(L, "no self pointer");
669 if (!lua_isstring(L, -1))
672 appid = lua_tostring(L, -1);
677 mrp_log_info("get_lastinfo: appid: '%s'", appid);
679 if (!get_lastinfo(L, appid))
685 return luaL_error(L, "get_lastinfo error");
688 static char *create_home_dir(const char *dir_path, const char *name)
690 int dirpath_len = strlen(dir_path);
691 int name_len = strlen(name);
692 int user_dir_path_len = dirpath_len + 1 + name_len;
693 char user_dir_path[user_dir_path_len + 1];
696 mrp_debug("create user home directory: %s, %s", dir_path, name);
698 if (stat(dir_path, &buf) == -1) {
700 if (errno != ENOENT) {
701 mrp_log_error("cannot access user data directory '%s'", dir_path);
705 if (!mkdir_if_needed(dir_path)) {
706 mrp_log_error("could not create user data directory '%s'", dir_path);
711 strcpy(user_dir_path, dir_path);
712 user_dir_path[dirpath_len] = '/';
713 strcpy(user_dir_path + dirpath_len + 1, name);
714 user_dir_path[user_dir_path_len] = '\0';
716 if (stat(user_dir_path, &buf) == -1) {
718 if (errno != ENOENT) {
719 mrp_log_error("cannot access private user data directory '%s'",
724 if (!mkdir_if_needed(user_dir_path)) {
725 mrp_log_error("could not create private user data directory '%s'",
731 return mrp_strdup(user_dir_path);
734 static void delete_conf(user_config_t *conf)
739 mrp_free(conf->homescreen);
740 mrp_free(conf->name);
741 mrp_free(conf->passwd);
742 mrp_free(conf->user_dir);
746 static bool parse_config_file(const char *filename, char **default_user_name,
747 user_manager_config_t *ctx)
749 xmlDocPtr doc = xmlParseFile(filename);
750 xmlNodePtr root = NULL;
754 mrp_log_error("Error parsing document\n");
758 root = xmlDocGetRootElement(doc);
761 mrp_log_error("no root node in the document\n");
766 if (xmlStrcmp((xmlChar *) "userconfig", root->name) == 0) {
767 xmlNodePtr section = root->xmlChildrenNode;
769 /* can contain "user", "default", and "homescreens" */
772 mrp_log_error("no section\n");
777 if (xmlStrcmp((xmlChar *) "users", section->name) == 0) {
778 xmlNodePtr xusers = section->xmlChildrenNode;
781 mrp_log_error("no users\n");
786 if (xmlStrcmp((xmlChar *) "user", xusers->name) == 0) {
787 xmlNodePtr user_data = xusers->xmlChildrenNode;
788 user_config_t *conf = mrp_allocz(sizeof(user_config_t));
791 mrp_log_error("no user_data\n");
799 mrp_list_init(&conf->hook);
803 if (xmlStrcmp((xmlChar *) "name", user_data->name) == 0) {
805 name = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
810 conf->name = mrp_strdup((char *) name);
813 else if (xmlStrcmp((xmlChar *) "passwd", user_data->name) == 0) {
815 passwd = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
816 conf->passwd = passwd ? mrp_strdup((char *) passwd) : mrp_strdup("");
819 else if (xmlStrcmp((xmlChar *) "hs", user_data->name) == 0) {
821 hs = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
826 conf->homescreen = mrp_strdup((char *) hs);
829 } while ((user_data = user_data->next));
831 /* check if we have all the data we need */
832 if (!(conf->name && conf->passwd && conf->homescreen)) {
833 mrp_log_error("incomplete user data");
837 mrp_list_append(&users, &conf->hook);
840 } while ((xusers = xusers->next));
842 else if (xmlStrcmp((xmlChar *) "default", section->name) == 0) {
843 xmlNodePtr default_user = section->xmlChildrenNode;
846 mrp_log_error("no default_user\n");
851 if (xmlStrcmp((xmlChar *) "user", default_user->name) == 0) {
852 xmlNodePtr user_data = default_user->xmlChildrenNode;
855 mrp_log_error("no user_data\n");
859 if (xmlStrcmp((xmlChar *) "name", user_data->name) == 0) {
861 name = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
864 *default_user_name = mrp_strdup((char *) name);
867 } while ((user_data = user_data->next));
869 } while ((default_user = default_user->next));
871 } while ((section = section->next));
872 } /* if "user_config" */
881 static void user_manager_config_free(user_manager_config_t *ctx)
883 mrp_free(ctx->defaultapps_path);
884 mrp_free(ctx->flag_file_path);
885 mrp_free(ctx->user_dir_base);
890 static void find_hs(user_manager_config_t *ctx, mql_result_t *select_r,
891 mqi_event_type_t evtype)
895 if (!select_r || select_r->type != mql_result_rows)
898 n_rows = mql_result_rows_get_row_count(select_r);
900 for (i = 0; i < n_rows; i++) {
904 pid = mql_result_rows_get_integer(select_r, 0, i);
905 appid = mql_result_rows_get_string(select_r, 1, i, NULL, 0);
907 mrp_log_info("application %s (pid: %d) running",
908 appid ? appid : "NULL", pid);
913 if (strcmp(appid, current_user->homescreen) == 0) {
914 if (evtype == mqi_row_inserted) {
915 ctx->current_hs_pid = pid;
916 mrp_log_info("set current homescreen pid to %d", pid);
918 else if (evtype == mqi_row_deleted) {
919 ctx->current_hs_pid = -1;
920 mrp_log_info("reset current homescreen pid");
927 static void application_event_cb(mql_result_t *result, void *user_data)
929 user_manager_config_t *ctx = (user_manager_config_t *) user_data;
931 if (!current_user || !current_user->homescreen)
934 if (result->type == mql_result_event) {
936 mqi_event_type_t evtype = mql_result_event_get_type(result);
938 /* If the result value is asked as a string, this is what we get:
941 -------------------------------
942 'row deleted' aul_applications
945 ----------------------------------------------------------
946 369 org.tizen.ico.onscreen
949 find_hs(ctx, mql_result_event_get_changed_rows(result), evtype);
953 static bool user_init(mrp_mainloop_t *ml, const char *filename,
954 const char *user_dir)
956 char *default_user = NULL;
958 mrp_list_hook_t *p, *n;
959 int user_dir_len, ret;
960 user_manager_config_t *ctx;
963 const char *trigger_s = "CREATE TRIGGER row_trigger"
964 " ON ROWS IN aul_applications"
965 " CALLBACK application_event_cb"
966 " SELECT pid, appid";
967 const char *select_s = "SELECT pid, appid FROM aul_applications";
969 user_dir_len = strlen(user_dir);
971 ctx = mrp_allocz(sizeof(user_manager_config_t));
977 ctx->current_hs_pid = -1; /* unknown still */
979 ctx->user_dir_base = mrp_strdup(user_dir);
980 ctx->defaultapps_path =
981 mrp_allocz(user_dir_len + 1 + strlen("defaultApps.info") + 1);
983 if (!ctx->user_dir_base || !ctx->defaultapps_path) {
984 user_manager_config_free(ctx);
988 /* TODO: do a routine for generating and validating paths, since we appear
989 to be creating a ton of them */
991 memcpy(ctx->defaultapps_path, user_dir, user_dir_len);
992 ctx->defaultapps_path[user_dir_len] = '/';
993 memcpy(ctx->defaultapps_path+user_dir_len+1, "defaultApps.info",
994 strlen("defaultApps.info"));
996 /* load the user data (username and password pairs) from a file */
998 mrp_log_info("user data parsing from %s", filename);
1000 mrp_list_init(&users);
1002 if (!parse_config_file(filename, &default_user, ctx)) {
1003 mrp_free(default_user);
1004 user_manager_config_free(ctx);
1005 mrp_log_error("parsing the user.xml file failed!");
1009 /* create user dirs and set the default user */
1011 mrp_list_foreach(&users, p, n) {
1012 user_config_t *config;
1013 config = mrp_list_entry(p, typeof(*config), hook);
1015 config->user_dir = create_home_dir(user_dir, config->name);
1017 if (!config->user_dir) {
1018 user_manager_config_free(ctx);
1022 config->runningapp_path =
1023 mrp_allocz(strlen(config->user_dir) + 1 + strlen("runningapp.info") + 1);
1025 if (!config->runningapp_path) {
1026 user_manager_config_free(ctx);
1030 memcpy(config->runningapp_path, config->user_dir, strlen(config->user_dir));
1031 config->runningapp_path[strlen(config->user_dir)] = '/';
1032 memcpy(config->runningapp_path+strlen(config->user_dir)+1,
1033 "runningapp.info", strlen("runningapp.info"));
1036 if (!current_user) {
1037 /* point to the first user by default */
1038 current_user = config;
1042 if (default_user && strcmp(default_user, config->name) == 0)
1043 current_user = config;
1046 mrp_free(default_user);
1048 /* create tmp dir for flag file */
1050 if (!mkdir_if_needed("/tmp/ico")) {
1054 ctx->flag_file_path = mrp_strdup("/tmp/ico/changeUser.flag");
1055 if (!ctx->flag_file_path) {
1059 ret = mql_register_callback("application_event_cb", mql_result_event,
1060 application_event_cb, ctx);
1063 mrp_log_error("failed to register database trigger");
1067 tx = mqi_begin_transaction();
1069 r = mql_exec_string(mql_result_string, trigger_s);
1071 if (!mql_result_is_success(r)) {
1072 mrp_log_error("db error: %s", mql_result_error_get_message(r));
1077 mqi_commit_transaction(tx);
1079 /* get the current list of running applications and see if homescreen
1082 tx = mqi_begin_transaction();
1084 r = mql_exec_string(mql_result_rows, select_s);
1086 if (!mql_result_is_success(r)) {
1087 mrp_log_error("db error: %s", mql_result_error_get_message(r));
1089 /* find the home screen */
1090 find_hs(ctx, r, mqi_row_inserted);
1098 mqi_commit_transaction(tx);
1103 /* TODO: delete also the user list */
1104 user_manager_config_free(ctx);
1108 static bool user_deinit()
1110 mrp_list_hook_t *p, *n;
1112 /* TODO: stop ongoing timers */
1114 /* go through users and free user configs */
1116 mrp_list_foreach(&users, p, n) {
1117 user_config_t *config;
1118 config = mrp_list_entry(p, typeof(*config), hook);
1120 mrp_list_delete(&config->hook);
1122 delete_conf(config);
1125 /* TODO: delete the ctx */
1130 bool mrp_user_scripting_init(lua_State *L, const char *config_file,
1131 const char *lastinfo_dir, mrp_mainloop_t *ml)
1135 return user_init(ml, config_file, lastinfo_dir);
1138 void mrp_user_scripting_deinit(lua_State *L)
1145 MURPHY_REGISTER_LUA_BINDINGS(murphy, USER_MANAGER_CLASS,
1146 { "UserManager", user_manager_create });