s3:registry: move the reg_api prototypes to their own header.
[kai/samba.git] / source3 / services / services_db.c
1 /* 
2  *  Unix SMB/CIFS implementation.
3  *  Service Control API Implementation
4  * 
5  *  Copyright (C) Marcin Krzysztof Porwit         2005.
6  *  Largely Rewritten by:
7  *  Copyright (C) Gerald (Jerry) Carter           2005.
8  *  
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 3 of the License, or
12  *  (at your option) any later version.
13  *  
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *  
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "includes.h"
24 #include "services/services.h"
25 #include "registry.h"
26 #include "registry/reg_api.h"
27 #include "registry/reg_api_util.h"
28
29 struct rcinit_file_information {
30         char *description;
31 };
32
33 struct service_display_info {
34         const char *servicename;
35         const char *daemon;
36         const char *dispname;
37         const char *description;
38 };
39
40 struct service_display_info builtin_svcs[] = {
41   { "Spooler",        "smbd",   "Print Spooler", "Internal service for spooling files to print devices" },
42   { "NETLOGON",       "smbd",   "Net Logon", "File service providing access to policy and profile data (not remotely manageable)" },
43   { "RemoteRegistry", "smbd",   "Remote Registry Service", "Internal service providing remote access to "
44                                 "the Samba registry" },
45   { "WINS",           "nmbd",   "Windows Internet Name Service (WINS)", "Internal service providing a "
46                                 "NetBIOS point-to-point name server (not remotely manageable)" },
47   { NULL, NULL, NULL, NULL }
48 };
49
50 struct service_display_info common_unix_svcs[] = {
51   { "cups",          NULL, "Common Unix Printing System","Provides unified printing support for all operating systems" },
52   { "postfix",       NULL, "Internet Mail Service",     "Provides support for sending and receiving electonic mail" },
53   { "sendmail",      NULL, "Internet Mail Service",     "Provides support for sending and receiving electonic mail" },
54   { "portmap",       NULL, "TCP Port to RPC PortMapper",NULL },
55   { "xinetd",        NULL, "Internet Meta-Daemon",      NULL },
56   { "inet",          NULL, "Internet Meta-Daemon",      NULL },
57   { "xntpd",         NULL, "Network Time Service",      NULL },
58   { "ntpd",          NULL, "Network Time Service",      NULL },
59   { "lpd",           NULL, "BSD Print Spooler",         NULL },
60   { "nfsserver",     NULL, "Network File Service",      NULL },
61   { "cron",          NULL, "Scheduling Service",        NULL },
62   { "at",            NULL, "Scheduling Service",        NULL },
63   { "nscd",          NULL, "Name Service Cache Daemon", NULL },
64   { "slapd",         NULL, "LDAP Directory Service",    NULL },
65   { "ldap",          NULL, "LDAP DIrectory Service",    NULL },
66   { "ypbind",        NULL, "NIS Directory Service",     NULL },
67   { "courier-imap",  NULL, "IMAP4 Mail Service",        NULL },
68   { "courier-pop3",  NULL, "POP3 Mail Service",         NULL },
69   { "named",         NULL, "Domain Name Service",       NULL },
70   { "bind",          NULL, "Domain Name Service",       NULL },
71   { "httpd",         NULL, "HTTP Server",               NULL },
72   { "apache",        NULL, "HTTP Server",               "Provides s highly scalable and flexible web server "
73                                                         "capable of implementing various protocols incluing "
74                                                         "but not limited to HTTP" },
75   { "autofs",        NULL, "Automounter",               NULL },
76   { "squid",         NULL, "Web Cache Proxy ",          NULL },
77   { "perfcountd",    NULL, "Performance Monitoring Daemon", NULL },
78   { "pgsql",         NULL, "PgSQL Database Server",     "Provides service for SQL database from Postgresql.org" },
79   { "arpwatch",      NULL, "ARP Tables watcher",        "Provides service for monitoring ARP tables for changes" },
80   { "dhcpd",         NULL, "DHCP Server",               "Provides service for dynamic host configuration and IP assignment" },
81   { "nwserv",        NULL, "NetWare Server Emulator",   "Provides service for emulating Novell NetWare 3.12 server" },
82   { "proftpd",       NULL, "Professional FTP Server",   "Provides high configurable service for FTP connection and "
83                                                         "file transferring" },
84   { "ssh2",          NULL, "SSH Secure Shell",          "Provides service for secure connection for remote administration" },
85   { "sshd",          NULL, "SSH Secure Shell",          "Provides service for secure connection for remote administration" },
86   { NULL, NULL, NULL, NULL }
87 };
88
89 static WERROR svcctl_set_secdesc_internal(struct registry_key *key,
90                                           struct security_descriptor *sec_desc);
91
92 /********************************************************************
93 ********************************************************************/
94
95 static struct security_descriptor* construct_service_sd( TALLOC_CTX *ctx )
96 {
97         struct security_ace ace[4];
98         size_t i = 0;
99         struct security_descriptor *sd = NULL;
100         struct security_acl *theacl = NULL;
101         size_t sd_size;
102
103         /* basic access for Everyone */
104
105         init_sec_ace(&ace[i++], &global_sid_World,
106                 SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_READ_ACCESS, 0);
107
108         init_sec_ace(&ace[i++], &global_sid_Builtin_Power_Users,
109                         SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_EXECUTE_ACCESS, 0);
110
111         init_sec_ace(&ace[i++], &global_sid_Builtin_Server_Operators,
112                 SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_ALL_ACCESS, 0);
113         init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
114                 SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_ALL_ACCESS, 0);
115
116         /* create the security descriptor */
117
118         theacl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace);
119         if (theacl == NULL) {
120                 return NULL;
121         }
122
123         sd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1,
124                            SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL,
125                            theacl, &sd_size);
126         if (sd == NULL) {
127                 return NULL;
128         }
129
130         return sd;
131 }
132
133 /********************************************************************
134  This is where we do the dirty work of filling in things like the
135  Display name, Description, etc...
136 ********************************************************************/
137
138 static char *get_common_service_dispname(TALLOC_CTX *mem_ctx,
139                                          const char *servicename)
140 {
141         int i;
142
143         for ( i=0; common_unix_svcs[i].servicename; i++ ) {
144                 if (strequal(servicename, common_unix_svcs[i].servicename)) {
145                         char *dispname;
146                         dispname = talloc_asprintf(mem_ctx, "%s (%s)",
147                                         common_unix_svcs[i].dispname,
148                                         common_unix_svcs[i].servicename);
149                         if (dispname == NULL) {
150                                 return NULL;
151                         }
152                         return dispname;
153                 }
154         }
155
156         return talloc_strdup(mem_ctx, servicename);
157 }
158
159 /********************************************************************
160 ********************************************************************/
161
162 static char *cleanup_string( const char *string )
163 {
164         char *clean = NULL;
165         char *begin, *end;
166         TALLOC_CTX *ctx = talloc_tos();
167
168         clean = talloc_strdup(ctx, string);
169         if (!clean) {
170                 return NULL;
171         }
172         begin = clean;
173
174         /* trim any beginning whilespace */
175
176         while (isspace(*begin)) {
177                 begin++;
178         }
179
180         if (*begin == '\0') {
181                 return NULL;
182         }
183
184         /* trim any trailing whitespace or carriage returns.
185            Start at the end and move backwards */
186
187         end = begin + strlen(begin) - 1;
188
189         while ( isspace(*end) || *end=='\n' || *end=='\r' ) {
190                 *end = '\0';
191                 end--;
192         }
193
194         return begin;
195 }
196
197 /********************************************************************
198 ********************************************************************/
199
200 static bool read_init_file( const char *servicename, struct rcinit_file_information **service_info )
201 {
202         struct rcinit_file_information *info = NULL;
203         char *filepath = NULL;
204         char str[1024];
205         XFILE *f = NULL;
206         char *p = NULL;
207
208         info = TALLOC_ZERO_P( NULL, struct rcinit_file_information );
209         if (info == NULL) {
210                 return False;
211         }
212
213         /* attempt the file open */
214
215         filepath = talloc_asprintf(info, "%s/%s/%s", get_dyn_MODULESDIR(),
216                                 SVCCTL_SCRIPT_DIR, servicename);
217         if (!filepath) {
218                 TALLOC_FREE(info);
219                 return false;
220         }
221         f = x_fopen( filepath, O_RDONLY, 0 );
222         if (f == NULL) {
223                 DEBUG(0,("read_init_file: failed to open [%s]\n", filepath));
224                 TALLOC_FREE(info);
225                 return false;
226         }
227
228         while ( (x_fgets( str, sizeof(str)-1, f )) != NULL ) {
229                 /* ignore everything that is not a full line
230                    comment starting with a '#' */
231
232                 if ( str[0] != '#' )
233                         continue;
234
235                 /* Look for a line like '^#.*Description:' */
236
237                 p = strstr( str, "Description:" );
238                 if (p != NULL) {
239                         char *desc;
240
241                         p += strlen( "Description:" ) + 1;
242                         if ( !p )
243                                 break;
244
245                         desc = cleanup_string(p);
246                         if (desc != NULL)
247                                 info->description = talloc_strdup( info, desc );
248                 }
249         }
250
251         x_fclose( f );
252
253         if ( !info->description )
254                 info->description = talloc_strdup( info, "External Unix Service" );
255
256         *service_info = info;
257         TALLOC_FREE(filepath);
258
259         return True;
260 }
261
262 /********************************************************************
263  This is where we do the dirty work of filling in things like the
264  Display name, Description, etc...
265 ********************************************************************/
266
267 static WERROR svcctl_setvalue(struct registry_key *key,
268                               const char *name,
269                               struct registry_value *value)
270 {
271         WERROR wresult;
272
273         wresult = reg_setvalue(key, name, value);
274         if (!W_ERROR_IS_OK(wresult)) {
275                 DEBUG(0, ("reg_setvalue failed for %s in key %s: %s\n",
276                           name, key->key->name, win_errstr(wresult)));
277         }
278
279         return wresult;
280 }
281
282 static WERROR svcctl_setvalue_dword(struct registry_key *key,
283                                     const char *name,
284                                     uint32_t dword)
285 {
286         struct registry_value value;
287
288         value.type = REG_DWORD;
289         value.data.length = sizeof(uint32_t);
290         value.data.data = (uint8_t *)&dword;
291
292         return svcctl_setvalue(key, name, &value);
293 }
294
295 static WERROR svcctl_setvalue_sz(struct registry_key *key,
296                                  const char *name,
297                                  const char *sz)
298 {
299         struct registry_value value;
300         WERROR wresult;
301         TALLOC_CTX *mem_ctx = talloc_stackframe();
302
303         if (!push_reg_sz(mem_ctx, &value.data, sz)) {
304                 DEBUG(0, ("push_reg_sz failed\n"));
305                 wresult = WERR_NOMEM;
306                 goto done;
307         }
308         value.type = REG_SZ;
309
310         wresult = svcctl_setvalue(key, name, &value);
311 done:
312         talloc_free(mem_ctx);
313         return wresult;
314 }
315
316 static void fill_service_values(struct registry_key *key)
317 {
318         char *dname, *ipath, *description;
319         int i;
320         WERROR wresult;
321         TALLOC_CTX *mem_ctx = talloc_stackframe();
322         char *name = NULL;
323
324         name = strrchr(key->key->name, '\\');
325         if (name == NULL) {
326                 name = key->key->name;
327         } else {
328                 name++;
329         }
330
331         /* These values are hardcoded in all QueryServiceConfig() replies.
332            I'm just storing them here for cosmetic purposes */
333
334         wresult = svcctl_setvalue_dword(key, "Start", SVCCTL_AUTO_START);
335         if (!W_ERROR_IS_OK(wresult)) {
336                 goto done;
337         }
338
339         wresult = svcctl_setvalue_dword(key, "Type", SERVICE_TYPE_WIN32_OWN_PROCESS);
340         if (!W_ERROR_IS_OK(wresult)) {
341                 goto done;
342         }
343
344         wresult = svcctl_setvalue_dword(key, "ErrorControl", SVCCTL_SVC_ERROR_NORMAL);
345         if (!W_ERROR_IS_OK(wresult)) {
346                 goto done;
347         }
348
349         /* everything runs as LocalSystem */
350
351         wresult = svcctl_setvalue_sz(key, "ObjectName", "LocalSystem");
352         if (!W_ERROR_IS_OK(wresult)) {
353                 goto done;
354         }
355
356         /* special considerations for internal services and the DisplayName value */
357
358         for ( i=0; builtin_svcs[i].servicename; i++ ) {
359                 if ( strequal( name, builtin_svcs[i].servicename ) ) {
360                         ipath = talloc_asprintf(mem_ctx, "%s/%s/%s",
361                                                 get_dyn_MODULESDIR(),
362                                                 SVCCTL_SCRIPT_DIR,
363                                                 builtin_svcs[i].daemon);
364                         description = talloc_strdup(mem_ctx, builtin_svcs[i].description);
365                         dname = talloc_strdup(mem_ctx, builtin_svcs[i].dispname);
366                         break;
367                 }
368         }
369
370         /* default to an external service if we haven't found a match */
371
372         if ( builtin_svcs[i].servicename == NULL ) {
373                 char *dispname = NULL;
374                 struct rcinit_file_information *init_info = NULL;
375
376                 ipath = talloc_asprintf(mem_ctx, "%s/%s/%s",
377                                         get_dyn_MODULESDIR(), SVCCTL_SCRIPT_DIR,
378                                         name);
379
380                 /* lookup common unix display names */
381                 dispname = get_common_service_dispname(mem_ctx, name);
382                 dname = talloc_strdup(mem_ctx, dispname ? dispname : "");
383
384                 /* get info from init file itself */
385                 if ( read_init_file( name, &init_info ) ) {
386                         description = talloc_strdup(mem_ctx, init_info->description);
387                         TALLOC_FREE( init_info );
388                 }
389                 else {
390                         description = talloc_strdup(mem_ctx, "External Unix Service");
391                 }
392         }
393
394         /* add the new values */
395
396         wresult = svcctl_setvalue_sz(key, "DisplayName", dname);
397         if (!W_ERROR_IS_OK(wresult)) {
398                 goto done;
399         }
400
401         wresult = svcctl_setvalue_sz(key, "ImagePath", ipath);
402         if (!W_ERROR_IS_OK(wresult)) {
403                 goto done;
404         }
405
406         wresult = svcctl_setvalue_sz(key, "Description", description);
407
408 done:
409         talloc_free(mem_ctx);
410         return;
411 }
412
413 /********************************************************************
414 ********************************************************************/
415
416 static void add_new_svc_name(struct registry_key *key_parent,
417                              const char *name)
418 {
419         struct registry_key *key_service = NULL, *key_secdesc = NULL;
420         WERROR wresult;
421         struct security_descriptor *sd = NULL;
422         TALLOC_CTX *mem_ctx = talloc_stackframe();
423         enum winreg_CreateAction action = REG_ACTION_NONE;
424
425         wresult = reg_createkey(mem_ctx, key_parent, name, REG_KEY_ALL,
426                                 &key_service, &action);
427
428         if (!W_ERROR_IS_OK(wresult)) {
429                 DEBUG(0, ("add_new_svc_name: reg_createkey failed for %s\\%s: "
430                           "%s\n", key_parent->key->name, name,
431                           win_errstr(wresult)));
432                 goto done;
433         }
434
435         /* now for the service values */
436
437         fill_service_values(key_service);
438
439         /* now add the security descriptor */
440
441         sd = construct_service_sd(key_secdesc);
442         if (sd == NULL) {
443                 DEBUG(0, ("add_new_svc_name: Failed to create default "
444                           "sec_desc!\n"));
445                 goto done;
446         }
447
448         wresult = svcctl_set_secdesc_internal(key_service, sd);
449
450 done:
451         talloc_free(mem_ctx);
452         return;
453 }
454
455 /********************************************************************
456 ********************************************************************/
457
458 void svcctl_init_keys( void )
459 {
460         const char **service_list = lp_svcctl_list();
461         int i;
462         struct registry_key *key = NULL;
463         struct registry_key *subkey = NULL;
464         WERROR wresult;
465         TALLOC_CTX *mem_ctx = talloc_stackframe();
466
467         /* bad mojo here if the lookup failed.  Should not happen */
468
469         wresult = reg_open_path(mem_ctx, KEY_SERVICES, REG_KEY_ALL, get_root_nt_token(), &key);
470
471         if ( !W_ERROR_IS_OK(wresult) ) {
472                 DEBUG(0,("svcctl_init_keys: key lookup failed! (%s)\n",
473                         win_errstr(wresult)));
474                 goto done;
475         }
476
477         /* the builtin services exist */
478
479         for ( i=0; builtin_svcs[i].servicename; i++ )
480                 add_new_svc_name(key, builtin_svcs[i].servicename);
481
482         for ( i=0; service_list && service_list[i]; i++ ) {
483
484                 /* only add new services */
485
486                 wresult = reg_openkey(mem_ctx, key, service_list[i], REG_KEY_ALL, &subkey);
487                 if (W_ERROR_IS_OK(wresult)) {
488                         continue;
489                 }
490
491                 /* Add the new service key and initialize the appropriate values */
492
493                 add_new_svc_name(key, service_list[i]);
494         }
495
496         /* initialize the control hooks */
497
498         init_service_op_table();
499
500 done:
501         talloc_free(mem_ctx);
502         return;
503 }
504
505 /********************************************************************
506  This is where we do the dirty work of filling in things like the
507  Display name, Description, etc...Always return a default secdesc
508  in case of any failure.
509 ********************************************************************/
510
511 struct security_descriptor *svcctl_get_secdesc( TALLOC_CTX *ctx, const char *name, struct security_token *token )
512 {
513         struct registry_key *key = NULL;
514         struct registry_value *value;
515         struct security_descriptor *ret_sd = NULL;
516         char *path= NULL;
517         WERROR wresult;
518         NTSTATUS status;
519         TALLOC_CTX *mem_ctx = talloc_stackframe();
520
521         path = talloc_asprintf(mem_ctx, "%s\\%s\\%s", KEY_SERVICES, name,
522                                "Security");
523         if (path == NULL) {
524                 goto done;
525         }
526
527         wresult = reg_open_path(mem_ctx, path, REG_KEY_ALL, token, &key);
528         if ( !W_ERROR_IS_OK(wresult) ) {
529                 DEBUG(0,("svcctl_get_secdesc: key lookup failed! [%s] (%s)\n",
530                         path, win_errstr(wresult)));
531                 goto done;
532         }
533
534         wresult = reg_queryvalue(mem_ctx, key, "Security", &value);
535         if (W_ERROR_EQUAL(wresult, WERR_BADFILE)) {
536                 goto fallback_to_default_sd;
537         } else if (!W_ERROR_IS_OK(wresult)) {
538                 DEBUG(0, ("svcctl_get_secdesc: error getting value 'Security': "
539                           "%s\n", win_errstr(wresult)));
540                 goto done;
541         }
542
543         status = unmarshall_sec_desc(ctx, value->data.data,
544                                      value->data.length, &ret_sd);
545
546         if (NT_STATUS_IS_OK(status)) {
547                 goto done;
548         }
549
550 fallback_to_default_sd:
551         DEBUG(6, ("svcctl_get_secdesc: constructing default secdesc for "
552                   "service [%s]\n", name));
553         ret_sd = construct_service_sd(ctx);
554
555 done:
556         talloc_free(mem_ctx);
557         return ret_sd;
558 }
559
560 /********************************************************************
561  Wrapper to make storing a Service sd easier
562 ********************************************************************/
563
564 static WERROR svcctl_set_secdesc_internal(struct registry_key *key,
565                                           struct security_descriptor *sec_desc)
566 {
567         struct registry_key *key_security = NULL;
568         WERROR wresult;
569         struct registry_value value;
570         NTSTATUS status;
571         enum winreg_CreateAction action = REG_ACTION_NONE;
572         TALLOC_CTX *mem_ctx = talloc_stackframe();
573
574         wresult = reg_createkey(mem_ctx, key, "Security", REG_KEY_ALL, &key_security, &action);
575         if (!W_ERROR_IS_OK(wresult)) {
576                 DEBUG(0, ("svcctl_set_secdesc: reg_createkey failed: "
577                           "[%s\\Security] (%s)\n", key->key->name,
578                           win_errstr(wresult)));
579                 goto done;
580         }
581
582         status = marshall_sec_desc(mem_ctx, sec_desc, &value.data.data,
583                                    &value.data.length);
584         if (!NT_STATUS_IS_OK(status)) {
585                 DEBUG(0, ("svcctl_set_secdesc: marshall_sec_desc() failed: %s\n",
586                           nt_errstr(status)));
587                 wresult = ntstatus_to_werror(status);
588                 goto done;
589         }
590
591         value.type = REG_BINARY;
592
593         wresult = reg_setvalue(key_security, "Security", &value);
594         if (!W_ERROR_IS_OK(wresult)) {
595                 DEBUG(0, ("svcctl_set_secdesc: reg_setvalue failed: %s\n",
596                           win_errstr(wresult)));
597         }
598
599 done:
600         talloc_free(mem_ctx);
601         return wresult;
602 }
603
604 bool svcctl_set_secdesc(const char *name, struct security_descriptor *sec_desc,
605                         struct security_token *token)
606 {
607         struct registry_key *key = NULL;
608         WERROR wresult;
609         char *path = NULL;
610         bool ret = false;
611         TALLOC_CTX *mem_ctx = talloc_stackframe();
612
613         path = talloc_asprintf(mem_ctx, "%s\\%s", KEY_SERVICES, name);
614         if (path == NULL) {
615                 goto done;
616         }
617
618         wresult = reg_open_path(mem_ctx, path, REG_KEY_ALL, token, &key);
619         if ( !W_ERROR_IS_OK(wresult) ) {
620                 DEBUG(0, ("svcctl_set_secdesc: key lookup failed! [%s] (%s)\n",
621                           path, win_errstr(wresult)));
622                 goto done;
623         }
624
625         wresult = svcctl_set_secdesc_internal(key, sec_desc);
626
627         ret = W_ERROR_IS_OK(wresult);
628
629 done:
630         talloc_free(mem_ctx);
631         return ret;
632 }
633
634 const char *svcctl_get_string_value(TALLOC_CTX *ctx, const char *key_name,
635                                     const char *value_name,
636                                     struct security_token *token)
637 {
638         const char *result = NULL;
639         struct registry_key *key = NULL;
640         struct registry_value *value = NULL;
641         char *path = NULL;
642         WERROR wresult;
643         TALLOC_CTX *mem_ctx = talloc_stackframe();
644
645         path = talloc_asprintf(mem_ctx, "%s\\%s", KEY_SERVICES, key_name);
646         if (path == NULL) {
647                 goto done;
648         }
649
650         wresult = reg_open_path(mem_ctx, path, REG_KEY_READ, token, &key);
651         if (!W_ERROR_IS_OK(wresult)) {
652                 DEBUG(0, ("svcctl_get_string_value: key lookup failed! "
653                           "[%s] (%s)\n", path, win_errstr(wresult)));
654                 goto done;
655         }
656
657         wresult = reg_queryvalue(mem_ctx, key, value_name, &value);
658         if (!W_ERROR_IS_OK(wresult)) {
659                 DEBUG(0, ("svcctl_get_string_value: error getting value "
660                           "'%s': %s\n", value_name, win_errstr(wresult)));
661                 goto done;
662         }
663
664         if (value->type != REG_SZ) {
665                 goto done;
666         }
667
668         pull_reg_sz(ctx, &value->data, &result);
669
670         goto done;
671
672 done:
673         talloc_free(mem_ctx);
674         return result;
675 }
676
677 /********************************************************************
678 ********************************************************************/
679
680 const char *svcctl_lookup_dispname(TALLOC_CTX *ctx, const char *name, struct security_token *token )
681 {
682         const char *display_name = NULL;
683
684         display_name = svcctl_get_string_value(ctx, name, "DisplayName", token);
685
686         if (display_name == NULL) {
687                 display_name = talloc_strdup(ctx, name);
688         }
689
690         return display_name;
691 }
692
693 /********************************************************************
694 ********************************************************************/
695
696 const char *svcctl_lookup_description(TALLOC_CTX *ctx, const char *name, struct security_token *token )
697 {
698         const char *description = NULL;
699
700         description = svcctl_get_string_value(ctx, name, "Description", token);
701
702         if (description == NULL) {
703                 description = talloc_strdup(ctx, "Unix Service");
704         }
705
706         return description;
707 }