auth-worker-client.c revision 75b4cc30566e22675b9e7b19b15a7fd929d8f54c
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2005-2015 Dovecot authors, see the included COPYING file */
c0757c70cfd2c9b44de3504b753a4d2f38690ef0Timo Sirainen#define AUTH_WORKER_WARN_DISCONNECTED_LONG_CMD_SECS 30
12d38e76ba7f70d6219c89ec7416fea0d5de7e02Timo Sirainen#define CLIENT_STATE_STOP "waiting for shutdown"
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenstatic void auth_worker_input(struct auth_worker_client *client);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenstatic int auth_worker_output(struct auth_worker_client *client);
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainenvoid auth_worker_refresh_proctitle(const char *state)
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen if (!global_auth_settings->verbose_proctitle || !worker)
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen process_title_set(t_strdup_printf("worker: %s", state));
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenauth_worker_client_check_throttle(struct auth_worker_client *client)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (o_stream_get_buffer_used_size(client->output) >=
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* stop reading new requests until client has read the pending
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenworker_auth_request_new(struct auth_worker_client *client, unsigned int id,
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen const char *const *args)
83d2e37f065eabe38dc92db485c5ca39ee43ce05Timo Sirainen (void)auth_request_import(auth_request, *args, NULL);
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen (void)auth_request_import(auth_request, key, value);
798cfe56c9871262770384da1239162b3800cce1Timo Sirainenstatic void auth_worker_send_reply(struct auth_worker_client *client,
c0757c70cfd2c9b44de3504b753a4d2f38690ef0Timo Sirainen time_t cmd_duration = time(NULL) - client->cmd_start;
d6bffcdf187c155dccc04fb4267b4f82ce59347dTimo Sirainen const char *p;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(client->output, "RESTART\n");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(client->output, str_data(str), str_len(str));
c0757c70cfd2c9b44de3504b753a4d2f38690ef0Timo Sirainen if (o_stream_nfinish(client->output) < 0 && request != NULL &&
c0757c70cfd2c9b44de3504b753a4d2f38690ef0Timo Sirainen cmd_duration > AUTH_WORKER_WARN_DISCONNECTED_LONG_CMD_SECS) {
c0757c70cfd2c9b44de3504b753a4d2f38690ef0Timo Sirainen i_warning("Auth master disconnected us while handling "
d6bffcdf187c155dccc04fb4267b4f82ce59347dTimo Sirainen "request for %s for %ld secs (result=%s)",
a138ac12134564b151f00fdef86fba9cd9ba8af0Timo Sirainenreply_append_extra_fields(string_t *str, struct auth_request *request)
a138ac12134564b151f00fdef86fba9cd9ba8af0Timo Sirainen if (!auth_fields_is_empty(request->extra_fields)) {
a138ac12134564b151f00fdef86fba9cd9ba8af0Timo Sirainen auth_fields_append(request->extra_fields, str, 0, 0);
a138ac12134564b151f00fdef86fba9cd9ba8af0Timo Sirainen auth_fields_is_empty(request->userdb_reply)) {
a138ac12134564b151f00fdef86fba9cd9ba8af0Timo Sirainen /* all userdb_* fields had NULL values. we'll still
a138ac12134564b151f00fdef86fba9cd9ba8af0Timo Sirainen need to tell this to the master */
a138ac12134564b151f00fdef86fba9cd9ba8af0Timo Sirainen str_append(str, "\tuserdb_"AUTH_REQUEST_USER_KEY_IGNORE);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic void verify_plain_callback(enum passdb_result result,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct auth_worker_client *client = request->context;
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen if (request->failed && result == PASSDB_RESULT_OK)
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen if (result != PASSDB_RESULT_INTERNAL_FAILURE) {
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen str_append_tabescaped(str, request->passdb_password);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenauth_worker_handle_passv(struct auth_worker_client *client,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* verify plaintext password */
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen /* <passdb id> <password> [<args>] */
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_error("BUG: Auth worker server sent us invalid PASSV");
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen while (passdb != NULL && passdb->passdb->id != passdb_id)
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen /* could be a masterdb */
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen passdb = auth_request_get_auth(auth_request)->masterdbs;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen while (passdb != NULL && passdb->passdb->id != passdb_id)
b321df9603081896b70ec44635af96d674a9839aTimo Sirainen verify_plain(auth_request, password, verify_plain_callback);
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainenlookup_credentials_callback(enum passdb_result result,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct auth_worker_client *client = request->context;
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen if (request->failed && result == PASSDB_RESULT_OK)
3db05c8c00faca6ab9ac8391e1d6977365f4d1b3Timo Sirainen str_printfa(str, "{%s.b64}", request->credentials_scheme);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenauth_worker_handle_passl(struct auth_worker_client *client,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* lookup credentials */
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen /* <passdb id> <scheme> [<args>] */
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_error("BUG: Auth worker server sent us invalid PASSL");
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen auth_request->credentials_scheme = p_strdup(auth_request->pool, scheme);
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen while (auth_request->passdb->passdb->id != passdb_id) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen auth_request->passdb = auth_request->passdb->next;
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainen if (auth_request->passdb->passdb->iface.lookup_credentials == NULL) {
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainen i_error("BUG: PASSL lookup not supported by given passdb");
430c0b0c370bebeeceba2e206be76bc134742f41Timo Sirainen auth_request->prefer_plain_credentials = TRUE;
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen lookup_credentials(auth_request, lookup_credentials_callback);
484e12acec34f16e5a8adc001e23ae48f1dda8c7Timo Sirainenset_credentials_callback(bool success, struct auth_request *request)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen struct auth_worker_client *client = request->context;
484e12acec34f16e5a8adc001e23ae48f1dda8c7Timo Sirainen str_printfa(str, "%u\t%s\n", request->id, success ? "OK" : "FAIL");
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenauth_worker_handle_setcred(struct auth_worker_client *client,
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen /* <passdb id> <credentials> [<args>] */
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_error("BUG: Auth worker server sent us invalid SETCRED");
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_error("BUG: SETCRED had missing parameters");
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen while (auth_request->passdb->passdb->id != passdb_id) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen auth_request->passdb = auth_request->passdb->next;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_error("BUG: SETCRED had invalid passdb ID");
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen set_credentials(auth_request, creds, set_credentials_callback);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainenlookup_user_callback(enum userdb_result result,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct auth_worker_client *client = auth_request->context;
75b4cc30566e22675b9e7b19b15a7fd929d8f54cTimo Sirainen str_append_tabescaped(str, auth_request->user);
559f278a4c54d9fa7e0f2e96ebceda30562f9009Timo Sirainen auth_fields_append(auth_request->userdb_reply, str, 0, 0);
c0757c70cfd2c9b44de3504b753a4d2f38690ef0Timo Sirainen auth_worker_send_reply(client, auth_request, str);
f0c01ca67be18ed9c8011a094db2773f8795a1ebTimo Sirainenstatic struct auth_userdb *
f0c01ca67be18ed9c8011a094db2773f8795a1ebTimo Sirainenauth_userdb_find_by_id(struct auth_userdb *userdbs, unsigned int id)
f0c01ca67be18ed9c8011a094db2773f8795a1ebTimo Sirainen for (db = userdbs; db != NULL; db = db->next) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenauth_worker_handle_user(struct auth_worker_client *client,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* lookup user */
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen /* <userdb id> [<args>] */
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen i_error("BUG: Auth worker server sent us invalid USER");
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 1);
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
f0c01ca67be18ed9c8011a094db2773f8795a1ebTimo Sirainen auth_userdb_find_by_id(auth_request->userdb, userdb_id);
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainenauth_worker_client_idle_kill(struct auth_worker_client *client ATTR_UNUSED)
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainenauth_worker_client_set_idle_timeout(struct auth_worker_client *client)
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainen idle_kill_secs = master_service_get_idle_kill_secs(master_service);
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainen client->to_idle = timeout_add(idle_kill_secs * 1000,
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenstatic void list_iter_deinit(struct auth_worker_list_context *ctx)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen struct auth_worker_client *client = ctx->client;
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen if (ctx->auth_request->userdb->userdb->iface->
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen str_printfa(str, "%u\tFAIL\n", ctx->auth_request->id);
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen str_printfa(str, "%u\tOK\n", ctx->auth_request->id);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen client->io = io_add(client->fd, IO_READ, auth_worker_input, client);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen o_stream_set_flush_callback(client->output, auth_worker_output, client);
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenstatic void list_iter_callback(const char *user, void *context)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen struct auth_worker_list_context *ctx = context;
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen str_printfa(str, "%u\t*\t%s\n", ctx->auth_request->id, user);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->client->output, str_data(str), str_len(str));
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen /* avoid recursively looping to this same function */
1ce47e48d7231da6f18f02eab6bab6451b4ef12aTimo Sirainen if (o_stream_get_buffer_used_size(ctx->client->output) > OUTBUF_THROTTLE_SIZE) {
1ce47e48d7231da6f18f02eab6bab6451b4ef12aTimo Sirainen if (o_stream_flush(ctx->client->output) < 0) {
1ce47e48d7231da6f18f02eab6bab6451b4ef12aTimo Sirainen o_stream_get_buffer_used_size(ctx->client->output) <= OUTBUF_THROTTLE_SIZE);
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainen o_stream_set_flush_pending(ctx->client->output, TRUE);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenstatic int auth_worker_list_output(struct auth_worker_list_context *ctx)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen if ((ret = o_stream_flush(ctx->client->output)) < 0) {
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenauth_worker_handle_list(struct auth_worker_client *client,
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen i_error("BUG: Auth worker server sent us invalid LIST");
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen userdb = auth_userdb_find_by_id(client->auth->userdbs, userdb_id);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen ctx = i_new(struct auth_worker_list_context, 1);
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen ctx->auth_request = worker_auth_request_new(client, id, args + 1);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen o_stream_set_flush_callback(ctx->client->output,
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen ctx->iter = ctx->auth_request->userdb->userdb->iface->
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen iterate_init(ctx->auth_request, list_iter_callback, ctx);
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen ctx->auth_request->userdb->userdb->iface->iterate_next(ctx->iter);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenauth_worker_handle_line(struct auth_worker_client *client, const char *line)
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen const char *const *args;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen unsigned int id;
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (args[0] == NULL || args[1] == NULL || args[2] == NULL ||
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen ret = auth_worker_handle_passv(client, id, args + 2);
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen ret = auth_worker_handle_passl(client, id, args + 2);
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen ret = auth_worker_handle_setcred(client, id, args + 2);
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen ret = auth_worker_handle_user(client, id, args + 2);
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen ret = auth_worker_handle_list(client, id, args + 2);
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen i_error("BUG: Auth-worker received unknown command: %s",
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainenstatic bool auth_worker_verify_db_hash(const char *line)
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen binary_to_hex_append(str, passdb_md5, sizeof(passdb_md5));
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen binary_to_hex_append(str, userdb_md5, sizeof(userdb_md5));
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic void auth_worker_input(struct auth_worker_client *client)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* disconnected */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* buffer full */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_error("BUG: Auth worker server sent us more than %d bytes",
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainen if (!version_string_verify(line, "auth-worker",
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen i_error("Auth worker not compatible with this server "
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen "(mixed old and new binaries?)");
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen i_error("Auth worker sees different passdbs/userdbs "
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen "than auth server. Maybe config just changed "
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen "and this goes away automatically?");
b624773984e35dd894db8dff976c1a2114c70782Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen while ((line = i_stream_next_line(client->input)) != NULL) {
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic int auth_worker_output(struct auth_worker_client *client)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (o_stream_get_buffer_used_size(client->output) <=
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen OUTBUF_THROTTLE_SIZE/3 && client->io == NULL) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* allow input again */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenauth_worker_client_create(struct auth *auth, int fd)
2526d52441ef368215ab6bf04fd0356d3b09d235Timo Sirainen client->input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH,
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen o_stream_set_flush_callback(client->output, auth_worker_output, client);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen client->io = io_add(fd, IO_READ, auth_worker_input, client);
b624773984e35dd894db8dff976c1a2114c70782Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_HANDSHAKE);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid auth_worker_client_destroy(struct auth_worker_client **_client)
7cba14a4c3beb026a2862ee50d24c554fa713329Timo Sirainen master_service_client_connection_destroyed(master_service);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid auth_worker_client_unref(struct auth_worker_client **_client)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(auth_worker_client->output, "ERROR\n");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(auth_worker_client->output, "SUCCESS\n");
5afc76d0215c5f7631dec06ef864d59f0686a0a8Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(auth_worker_client->output, "SHUTDOWN\n");