Git init
[framework/multimedia/pulseaudio.git] / src / utils / pactl.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <signal.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <limits.h>
34 #include <getopt.h>
35 #include <locale.h>
36
37 #include <sndfile.h>
38
39 #include <pulse/i18n.h>
40 #include <pulse/pulseaudio.h>
41
42 #include <pulsecore/macro.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/sndfile-util.h>
46
47 #define BUFSIZE (16*1024)
48
49 static pa_context *context = NULL;
50 static pa_mainloop_api *mainloop_api = NULL;
51
52 static char
53     *sample_name = NULL,
54     *sink_name = NULL,
55     *source_name = NULL,
56     *module_name = NULL,
57     *module_args = NULL,
58     *card_name = NULL,
59     *profile_name = NULL,
60     *port_name = NULL;
61
62 static uint32_t
63     sink_input_idx = PA_INVALID_INDEX,
64     source_output_idx = PA_INVALID_INDEX;
65
66 static uint32_t module_index;
67 static pa_bool_t suspend;
68 static pa_bool_t mute;
69 static pa_volume_t volume;
70
71 static pa_proplist *proplist = NULL;
72
73 static SNDFILE *sndfile = NULL;
74 static pa_stream *sample_stream = NULL;
75 static pa_sample_spec sample_spec;
76 static pa_channel_map channel_map;
77 static size_t sample_length = 0;
78 static int actions = 1;
79
80 static pa_bool_t nl = FALSE;
81
82 static enum {
83     NONE,
84     EXIT,
85     STAT,
86     UPLOAD_SAMPLE,
87     PLAY_SAMPLE,
88     REMOVE_SAMPLE,
89     LIST,
90     MOVE_SINK_INPUT,
91     MOVE_SOURCE_OUTPUT,
92     LOAD_MODULE,
93     UNLOAD_MODULE,
94     SUSPEND_SINK,
95     SUSPEND_SOURCE,
96     SET_CARD_PROFILE,
97     SET_SINK_PORT,
98     SET_SOURCE_PORT,
99     SET_SINK_VOLUME,
100     SET_SOURCE_VOLUME,
101     SET_SINK_INPUT_VOLUME,
102     SET_SINK_MUTE,
103     SET_SOURCE_MUTE,
104     SET_SINK_INPUT_MUTE
105 } action = NONE;
106
107 static void quit(int ret) {
108     pa_assert(mainloop_api);
109     mainloop_api->quit(mainloop_api, ret);
110 }
111
112 static void context_drain_complete(pa_context *c, void *userdata) {
113     pa_context_disconnect(c);
114 }
115
116 static void drain(void) {
117     pa_operation *o;
118
119     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
120         pa_context_disconnect(context);
121     else
122         pa_operation_unref(o);
123 }
124
125 static void complete_action(void) {
126     pa_assert(actions > 0);
127
128     if (!(--actions))
129         drain();
130 }
131
132 static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
133     char s[PA_BYTES_SNPRINT_MAX];
134     if (!i) {
135         pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
136         quit(1);
137         return;
138     }
139
140     pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
141     printf(_("Currently in use: %u blocks containing %s bytes total.\n"), i->memblock_total, s);
142
143     pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
144     printf(_("Allocated during whole lifetime: %u blocks containing %s bytes total.\n"), i->memblock_allocated, s);
145
146     pa_bytes_snprint(s, sizeof(s), i->scache_size);
147     printf(_("Sample cache size: %s\n"), s);
148
149     complete_action();
150 }
151
152 static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
153     char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
154
155     if (!i) {
156         pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
157         quit(1);
158         return;
159     }
160
161     pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
162     pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
163
164     printf(_("User name: %s\n"
165              "Host Name: %s\n"
166              "Server Name: %s\n"
167              "Server Version: %s\n"
168              "Default Sample Specification: %s\n"
169              "Default Channel Map: %s\n"
170              "Default Sink: %s\n"
171              "Default Source: %s\n"
172              "Cookie: %08x\n"),
173            i->user_name,
174            i->host_name,
175            i->server_name,
176            i->server_version,
177            ss,
178            cm,
179            i->default_sink_name,
180            i->default_source_name,
181            i->cookie);
182
183     complete_action();
184 }
185
186 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
187
188     static const char *state_table[] = {
189         [1+PA_SINK_INVALID_STATE] = "n/a",
190         [1+PA_SINK_RUNNING] = "RUNNING",
191         [1+PA_SINK_IDLE] = "IDLE",
192         [1+PA_SINK_SUSPENDED] = "SUSPENDED"
193     };
194
195     char
196         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
197         cv[PA_CVOLUME_SNPRINT_MAX],
198         cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
199         v[PA_VOLUME_SNPRINT_MAX],
200         vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
201         cm[PA_CHANNEL_MAP_SNPRINT_MAX];
202     char *pl;
203
204     if (is_last < 0) {
205         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
206         quit(1);
207         return;
208     }
209
210     if (is_last) {
211         complete_action();
212         return;
213     }
214
215     pa_assert(i);
216
217     if (nl)
218         printf("\n");
219     nl = TRUE;
220
221     printf(_("Sink #%u\n"
222              "\tState: %s\n"
223              "\tName: %s\n"
224              "\tDescription: %s\n"
225              "\tDriver: %s\n"
226              "\tSample Specification: %s\n"
227              "\tChannel Map: %s\n"
228              "\tOwner Module: %u\n"
229              "\tMute: %s\n"
230              "\tVolume: %s%s%s\n"
231              "\t        balance %0.2f\n"
232              "\tBase Volume: %s%s%s\n"
233              "\tMonitor Source: %s\n"
234              "\tLatency: %0.0f usec, configured %0.0f usec\n"
235              "\tFlags: %s%s%s%s%s%s\n"
236              "\tProperties:\n\t\t%s\n"),
237            i->index,
238            state_table[1+i->state],
239            i->name,
240            pa_strnull(i->description),
241            pa_strnull(i->driver),
242            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
243            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
244            i->owner_module,
245            pa_yes_no(i->mute),
246            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
247            i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
248            i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
249            pa_cvolume_get_balance(&i->volume, &i->channel_map),
250            pa_volume_snprint(v, sizeof(v), i->base_volume),
251            i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",
252            i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
253            pa_strnull(i->monitor_source_name),
254            (double) i->latency, (double) i->configured_latency,
255            i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
256            i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
257            i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
258            i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
259            i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
260            i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
261            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
262
263     pa_xfree(pl);
264
265     if (i->ports) {
266         pa_sink_port_info **p;
267
268         printf(_("\tPorts:\n"));
269         for (p = i->ports; *p; p++)
270             printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
271     }
272
273     if (i->active_port)
274         printf(_("\tActive Port: %s\n"),
275                i->active_port->name);
276 }
277
278 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
279
280     static const char *state_table[] = {
281         [1+PA_SOURCE_INVALID_STATE] = "n/a",
282         [1+PA_SOURCE_RUNNING] = "RUNNING",
283         [1+PA_SOURCE_IDLE] = "IDLE",
284         [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
285     };
286
287     char
288         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
289         cv[PA_CVOLUME_SNPRINT_MAX],
290         cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
291         v[PA_VOLUME_SNPRINT_MAX],
292         vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
293         cm[PA_CHANNEL_MAP_SNPRINT_MAX];
294     char *pl;
295
296     if (is_last < 0) {
297         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
298         quit(1);
299         return;
300     }
301
302     if (is_last) {
303         complete_action();
304         return;
305     }
306
307     pa_assert(i);
308
309     if (nl)
310         printf("\n");
311     nl = TRUE;
312
313     printf(_("Source #%u\n"
314              "\tState: %s\n"
315              "\tName: %s\n"
316              "\tDescription: %s\n"
317              "\tDriver: %s\n"
318              "\tSample Specification: %s\n"
319              "\tChannel Map: %s\n"
320              "\tOwner Module: %u\n"
321              "\tMute: %s\n"
322              "\tVolume: %s%s%s\n"
323              "\t        balance %0.2f\n"
324              "\tBase Volume: %s%s%s\n"
325              "\tMonitor of Sink: %s\n"
326              "\tLatency: %0.0f usec, configured %0.0f usec\n"
327              "\tFlags: %s%s%s%s%s%s\n"
328              "\tProperties:\n\t\t%s\n"),
329            i->index,
330            state_table[1+i->state],
331            i->name,
332            pa_strnull(i->description),
333            pa_strnull(i->driver),
334            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
335            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
336            i->owner_module,
337            pa_yes_no(i->mute),
338            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
339            i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t        " : "",
340            i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
341            pa_cvolume_get_balance(&i->volume, &i->channel_map),
342            pa_volume_snprint(v, sizeof(v), i->base_volume),
343            i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t             " : "",
344            i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
345            i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
346            (double) i->latency, (double) i->configured_latency,
347            i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
348            i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
349            i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
350            i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
351            i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
352            i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
353            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
354
355     pa_xfree(pl);
356
357     if (i->ports) {
358         pa_source_port_info **p;
359
360         printf(_("\tPorts:\n"));
361         for (p = i->ports; *p; p++)
362             printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
363     }
364
365     if (i->active_port)
366         printf(_("\tActive Port: %s\n"),
367                i->active_port->name);
368 }
369
370 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
371     char t[32];
372     char *pl;
373
374     if (is_last < 0) {
375         pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
376         quit(1);
377         return;
378     }
379
380     if (is_last) {
381         complete_action();
382         return;
383     }
384
385     pa_assert(i);
386
387     if (nl)
388         printf("\n");
389     nl = TRUE;
390
391     pa_snprintf(t, sizeof(t), "%u", i->n_used);
392
393     printf(_("Module #%u\n"
394              "\tName: %s\n"
395              "\tArgument: %s\n"
396              "\tUsage counter: %s\n"
397              "\tProperties:\n\t\t%s\n"),
398            i->index,
399            i->name,
400            i->argument ? i->argument : "",
401            i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
402            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
403
404     pa_xfree(pl);
405 }
406
407 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
408     char t[32];
409     char *pl;
410
411     if (is_last < 0) {
412         pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
413         quit(1);
414         return;
415     }
416
417     if (is_last) {
418         complete_action();
419         return;
420     }
421
422     pa_assert(i);
423
424     if (nl)
425         printf("\n");
426     nl = TRUE;
427
428     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
429
430     printf(_("Client #%u\n"
431              "\tDriver: %s\n"
432              "\tOwner Module: %s\n"
433              "\tProperties:\n\t\t%s\n"),
434            i->index,
435            pa_strnull(i->driver),
436            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
437            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
438
439     pa_xfree(pl);
440 }
441
442 static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
443     char t[32];
444     char *pl;
445
446     if (is_last < 0) {
447         pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
448         complete_action();
449         return;
450     }
451
452     if (is_last) {
453         complete_action();
454         return;
455     }
456
457     pa_assert(i);
458
459     if (nl)
460         printf("\n");
461     nl = TRUE;
462
463     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
464
465     printf(_("Card #%u\n"
466              "\tName: %s\n"
467              "\tDriver: %s\n"
468              "\tOwner Module: %s\n"
469              "\tProperties:\n\t\t%s\n"),
470            i->index,
471            i->name,
472            pa_strnull(i->driver),
473            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
474            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
475
476     if (i->profiles) {
477         pa_card_profile_info *p;
478
479         printf(_("\tProfiles:\n"));
480         for (p = i->profiles; p->name; p++)
481             printf("\t\t%s: %s (sinks: %u, sources: %u, priority. %u)\n", p->name, p->description, p->n_sinks, p->n_sources, p->priority);
482     }
483
484     if (i->active_profile)
485         printf(_("\tActive Profile: %s\n"),
486                i->active_profile->name);
487
488     pa_xfree(pl);
489 }
490
491 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
492     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
493     char *pl;
494
495     if (is_last < 0) {
496         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
497         quit(1);
498         return;
499     }
500
501     if (is_last) {
502         complete_action();
503         return;
504     }
505
506     pa_assert(i);
507
508     if (nl)
509         printf("\n");
510     nl = TRUE;
511
512     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
513     pa_snprintf(k, sizeof(k), "%u", i->client);
514
515     printf(_("Sink Input #%u\n"
516              "\tDriver: %s\n"
517              "\tOwner Module: %s\n"
518              "\tClient: %s\n"
519              "\tSink: %u\n"
520              "\tSample Specification: %s\n"
521              "\tChannel Map: %s\n"
522              "\tMute: %s\n"
523              "\tVolume: %s\n"
524              "\t        %s\n"
525              "\t        balance %0.2f\n"
526              "\tBuffer Latency: %0.0f usec\n"
527              "\tSink Latency: %0.0f usec\n"
528              "\tResample method: %s\n"
529              "\tProperties:\n\t\t%s\n"),
530            i->index,
531            pa_strnull(i->driver),
532            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
533            i->client != PA_INVALID_INDEX ? k : _("n/a"),
534            i->sink,
535            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
536            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
537            pa_yes_no(i->mute),
538            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
539            pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
540            pa_cvolume_get_balance(&i->volume, &i->channel_map),
541            (double) i->buffer_usec,
542            (double) i->sink_usec,
543            i->resample_method ? i->resample_method : _("n/a"),
544            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
545
546     pa_xfree(pl);
547 }
548
549 static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
550     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
551     char *pl;
552
553     if (is_last < 0) {
554         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
555         quit(1);
556         return;
557     }
558
559     if (is_last) {
560         complete_action();
561         return;
562     }
563
564     pa_assert(i);
565
566     if (nl)
567         printf("\n");
568     nl = TRUE;
569
570
571     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
572     pa_snprintf(k, sizeof(k), "%u", i->client);
573
574     printf(_("Source Output #%u\n"
575              "\tDriver: %s\n"
576              "\tOwner Module: %s\n"
577              "\tClient: %s\n"
578              "\tSource: %u\n"
579              "\tSample Specification: %s\n"
580              "\tChannel Map: %s\n"
581              "\tBuffer Latency: %0.0f usec\n"
582              "\tSource Latency: %0.0f usec\n"
583              "\tResample method: %s\n"
584              "\tProperties:\n\t\t%s\n"),
585            i->index,
586            pa_strnull(i->driver),
587            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
588            i->client != PA_INVALID_INDEX ? k : _("n/a"),
589            i->source,
590            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
591            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
592            (double) i->buffer_usec,
593            (double) i->source_usec,
594            i->resample_method ? i->resample_method : _("n/a"),
595            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
596
597     pa_xfree(pl);
598 }
599
600 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
601     char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
602     char *pl;
603
604     if (is_last < 0) {
605         pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
606         quit(1);
607         return;
608     }
609
610     if (is_last) {
611         complete_action();
612         return;
613     }
614
615     pa_assert(i);
616
617     if (nl)
618         printf("\n");
619     nl = TRUE;
620
621     pa_bytes_snprint(t, sizeof(t), i->bytes);
622
623     printf(_("Sample #%u\n"
624              "\tName: %s\n"
625              "\tSample Specification: %s\n"
626              "\tChannel Map: %s\n"
627              "\tVolume: %s\n"
628              "\t        %s\n"
629              "\t        balance %0.2f\n"
630              "\tDuration: %0.1fs\n"
631              "\tSize: %s\n"
632              "\tLazy: %s\n"
633              "\tFilename: %s\n"
634              "\tProperties:\n\t\t%s\n"),
635            i->index,
636            i->name,
637            pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
638            pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
639            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
640            pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
641            pa_cvolume_get_balance(&i->volume, &i->channel_map),
642            (double) i->duration/1000000.0,
643            t,
644            pa_yes_no(i->lazy),
645            i->filename ? i->filename : _("n/a"),
646            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
647
648     pa_xfree(pl);
649 }
650
651 static void simple_callback(pa_context *c, int success, void *userdata) {
652     if (!success) {
653         pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
654         quit(1);
655         return;
656     }
657
658     complete_action();
659 }
660
661 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
662     if (idx == PA_INVALID_INDEX) {
663         pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
664         quit(1);
665         return;
666     }
667
668     printf("%u\n", idx);
669
670     complete_action();
671 }
672
673 static void stream_state_callback(pa_stream *s, void *userdata) {
674     pa_assert(s);
675
676     switch (pa_stream_get_state(s)) {
677         case PA_STREAM_CREATING:
678         case PA_STREAM_READY:
679             break;
680
681         case PA_STREAM_TERMINATED:
682             drain();
683             break;
684
685         case PA_STREAM_FAILED:
686         default:
687             pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
688             quit(1);
689     }
690 }
691
692 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
693     sf_count_t l;
694     float *d;
695     pa_assert(s && length && sndfile);
696
697     d = pa_xmalloc(length);
698
699     pa_assert(sample_length >= length);
700     l = (sf_count_t) (length/pa_frame_size(&sample_spec));
701
702     if ((sf_readf_float(sndfile, d, l)) != l) {
703         pa_xfree(d);
704         pa_log(_("Premature end of file"));
705         quit(1);
706         return;
707     }
708
709     pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
710
711     sample_length -= length;
712
713     if (sample_length  <= 0) {
714         pa_stream_set_write_callback(sample_stream, NULL, NULL);
715         pa_stream_finish_upload(sample_stream);
716     }
717 }
718
719 static void context_state_callback(pa_context *c, void *userdata) {
720     pa_assert(c);
721     switch (pa_context_get_state(c)) {
722         case PA_CONTEXT_CONNECTING:
723         case PA_CONTEXT_AUTHORIZING:
724         case PA_CONTEXT_SETTING_NAME:
725             break;
726
727         case PA_CONTEXT_READY:
728             switch (action) {
729                 case STAT:
730                     actions = 2;
731                     pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
732                     pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
733                     break;
734
735                 case PLAY_SAMPLE:
736                     pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
737                     break;
738
739                 case REMOVE_SAMPLE:
740                     pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
741                     break;
742
743                 case UPLOAD_SAMPLE:
744                     sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
745                     pa_assert(sample_stream);
746
747                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
748                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
749                     pa_stream_connect_upload(sample_stream, sample_length);
750                     break;
751
752                 case EXIT:
753                     pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL));
754                     break;
755
756                 case LIST:
757                     actions = 8;
758                     pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
759                     pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
760                     pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
761                     pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
762                     pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
763                     pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
764                     pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
765                     pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
766                     break;
767
768                 case MOVE_SINK_INPUT:
769                     pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
770                     break;
771
772                 case MOVE_SOURCE_OUTPUT:
773                     pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
774                     break;
775
776                 case LOAD_MODULE:
777                     pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
778                     break;
779
780                 case UNLOAD_MODULE:
781                     pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
782                     break;
783
784                 case SUSPEND_SINK:
785                     if (sink_name)
786                         pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
787                     else
788                         pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
789                     break;
790
791                 case SUSPEND_SOURCE:
792                     if (source_name)
793                         pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
794                     else
795                         pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
796                     break;
797
798                 case SET_CARD_PROFILE:
799                     pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
800                     break;
801
802                 case SET_SINK_PORT:
803                     pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
804                     break;
805
806                 case SET_SOURCE_PORT:
807                     pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
808                     break;
809
810                 case SET_SINK_MUTE:
811                     pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
812                     break;
813
814                 case SET_SOURCE_MUTE:
815                     pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
816                     break;
817
818                 case SET_SINK_INPUT_MUTE:
819                     pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
820                     break;
821
822                 case SET_SINK_VOLUME: {
823                     pa_cvolume v;
824
825                     pa_cvolume_set(&v, 1, volume);
826                     pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
827                     break;
828                 }
829
830                 case SET_SOURCE_VOLUME: {
831                     pa_cvolume v;
832
833                     pa_cvolume_set(&v, 1, volume);
834                     pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
835                     break;
836                 }
837
838                 case SET_SINK_INPUT_VOLUME: {
839                     pa_cvolume v;
840
841                     pa_cvolume_set(&v, 1, volume);
842                     pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
843                     break;
844                 }
845
846                 default:
847                     pa_assert_not_reached();
848             }
849             break;
850
851         case PA_CONTEXT_TERMINATED:
852             quit(0);
853             break;
854
855         case PA_CONTEXT_FAILED:
856         default:
857             pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
858             quit(1);
859     }
860 }
861
862 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
863     pa_log(_("Got SIGINT, exiting."));
864     quit(0);
865 }
866
867 static void help(const char *argv0) {
868
869     printf(_("%s [options] stat\n"
870              "%s [options] list\n"
871              "%s [options] exit\n"
872              "%s [options] upload-sample FILENAME [NAME]\n"
873              "%s [options] play-sample NAME [SINK]\n"
874              "%s [options] remove-sample NAME\n"
875              "%s [options] move-sink-input SINKINPUT SINK\n"
876              "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
877              "%s [options] load-module NAME [ARGS ...]\n"
878              "%s [options] unload-module MODULE\n"
879              "%s [options] suspend-sink SINK 1|0\n"
880              "%s [options] suspend-source SOURCE 1|0\n"
881              "%s [options] set-card-profile CARD PROFILE\n"
882              "%s [options] set-sink-port SINK PORT\n"
883              "%s [options] set-source-port SOURCE PORT\n"
884              "%s [options] set-sink-volume SINK VOLUME\n"
885              "%s [options] set-source-volume SOURCE VOLUME\n"
886              "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
887              "%s [options] set-sink-mute SINK 1|0\n"
888              "%s [options] set-source-mute SOURCE 1|0\n"
889              "%s [options] set-sink-input-mute SINKINPUT 1|0\n\n"
890              "  -h, --help                            Show this help\n"
891              "      --version                         Show version\n\n"
892              "  -s, --server=SERVER                   The name of the server to connect to\n"
893              "  -n, --client-name=NAME                How to call this client on the server\n"),
894            argv0, argv0, argv0, argv0, argv0,
895            argv0, argv0, argv0, argv0, argv0,
896            argv0, argv0, argv0, argv0, argv0,
897            argv0, argv0, argv0, argv0, argv0,
898            argv0);
899 }
900
901 enum {
902     ARG_VERSION = 256
903 };
904
905 int main(int argc, char *argv[]) {
906     pa_mainloop* m = NULL;
907     int ret = 1, c;
908     char *server = NULL, *bn;
909
910     static const struct option long_options[] = {
911         {"server",      1, NULL, 's'},
912         {"client-name", 1, NULL, 'n'},
913         {"version",     0, NULL, ARG_VERSION},
914         {"help",        0, NULL, 'h'},
915         {NULL,          0, NULL, 0}
916     };
917
918     setlocale(LC_ALL, "");
919     bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
920
921     bn = pa_path_get_filename(argv[0]);
922
923     proplist = pa_proplist_new();
924
925     while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
926         switch (c) {
927             case 'h' :
928                 help(bn);
929                 ret = 0;
930                 goto quit;
931
932             case ARG_VERSION:
933                 printf(_("pactl %s\n"
934                          "Compiled with libpulse %s\n"
935                          "Linked with libpulse %s\n"),
936                        PACKAGE_VERSION,
937                        pa_get_headers_version(),
938                        pa_get_library_version());
939                 ret = 0;
940                 goto quit;
941
942             case 's':
943                 pa_xfree(server);
944                 server = pa_xstrdup(optarg);
945                 break;
946
947             case 'n': {
948                 char *t;
949
950                 if (!(t = pa_locale_to_utf8(optarg)) ||
951                     pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
952
953                     pa_log(_("Invalid client name '%s'"), t ? t : optarg);
954                     pa_xfree(t);
955                     goto quit;
956                 }
957
958                 pa_xfree(t);
959                 break;
960             }
961
962             default:
963                 goto quit;
964         }
965     }
966
967     if (optind < argc) {
968         if (pa_streq(argv[optind], "stat"))
969             action = STAT;
970         else if (pa_streq(argv[optind], "exit"))
971             action = EXIT;
972         else if (pa_streq(argv[optind], "list"))
973             action = LIST;
974         else if (pa_streq(argv[optind], "upload-sample")) {
975             struct SF_INFO sfi;
976             action = UPLOAD_SAMPLE;
977
978             if (optind+1 >= argc) {
979                 pa_log(_("Please specify a sample file to load"));
980                 goto quit;
981             }
982
983             if (optind+2 < argc)
984                 sample_name = pa_xstrdup(argv[optind+2]);
985             else {
986                 char *f = pa_path_get_filename(argv[optind+1]);
987                 sample_name = pa_xstrndup(f, strcspn(f, "."));
988             }
989
990             pa_zero(sfi);
991             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
992                 pa_log(_("Failed to open sound file."));
993                 goto quit;
994             }
995
996             if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
997                 pa_log(_("Failed to determine sample specification from file."));
998                 goto quit;
999             }
1000             sample_spec.format = PA_SAMPLE_FLOAT32;
1001
1002             if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1003                 if (sample_spec.channels > 2)
1004                      pa_log(_("Warning: Failed to determine sample specification from file."));
1005                 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1006             }
1007
1008             pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
1009             sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
1010
1011         } else if (pa_streq(argv[optind], "play-sample")) {
1012             action = PLAY_SAMPLE;
1013             if (argc != optind+2 && argc != optind+3) {
1014                 pa_log(_("You have to specify a sample name to play"));
1015                 goto quit;
1016             }
1017
1018             sample_name = pa_xstrdup(argv[optind+1]);
1019
1020             if (optind+2 < argc)
1021                 sink_name = pa_xstrdup(argv[optind+2]);
1022
1023         } else if (pa_streq(argv[optind], "remove-sample")) {
1024             action = REMOVE_SAMPLE;
1025             if (argc != optind+2) {
1026                 pa_log(_("You have to specify a sample name to remove"));
1027                 goto quit;
1028             }
1029
1030             sample_name = pa_xstrdup(argv[optind+1]);
1031
1032         } else if (pa_streq(argv[optind], "move-sink-input")) {
1033             action = MOVE_SINK_INPUT;
1034             if (argc != optind+3) {
1035                 pa_log(_("You have to specify a sink input index and a sink"));
1036                 goto quit;
1037             }
1038
1039             sink_input_idx = (uint32_t) atoi(argv[optind+1]);
1040             sink_name = pa_xstrdup(argv[optind+2]);
1041
1042         } else if (pa_streq(argv[optind], "move-source-output")) {
1043             action = MOVE_SOURCE_OUTPUT;
1044             if (argc != optind+3) {
1045                 pa_log(_("You have to specify a source output index and a source"));
1046                 goto quit;
1047             }
1048
1049             source_output_idx = (uint32_t) atoi(argv[optind+1]);
1050             source_name = pa_xstrdup(argv[optind+2]);
1051
1052         } else if (pa_streq(argv[optind], "load-module")) {
1053             int i;
1054             size_t n = 0;
1055             char *p;
1056
1057             action = LOAD_MODULE;
1058
1059             if (argc <= optind+1) {
1060                 pa_log(_("You have to specify a module name and arguments."));
1061                 goto quit;
1062             }
1063
1064             module_name = argv[optind+1];
1065
1066             for (i = optind+2; i < argc; i++)
1067                 n += strlen(argv[i])+1;
1068
1069             if (n > 0) {
1070                 p = module_args = pa_xmalloc(n);
1071
1072                 for (i = optind+2; i < argc; i++)
1073                     p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
1074             }
1075
1076         } else if (pa_streq(argv[optind], "unload-module")) {
1077             action = UNLOAD_MODULE;
1078
1079             if (argc != optind+2) {
1080                 pa_log(_("You have to specify a module index"));
1081                 goto quit;
1082             }
1083
1084             module_index = (uint32_t) atoi(argv[optind+1]);
1085
1086         } else if (pa_streq(argv[optind], "suspend-sink")) {
1087             action = SUSPEND_SINK;
1088
1089             if (argc > optind+3 || optind+1 >= argc) {
1090                 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
1091                 goto quit;
1092             }
1093
1094             suspend = pa_parse_boolean(argv[argc-1]);
1095
1096             if (argc > optind+2)
1097                 sink_name = pa_xstrdup(argv[optind+1]);
1098
1099         } else if (pa_streq(argv[optind], "suspend-source")) {
1100             action = SUSPEND_SOURCE;
1101
1102             if (argc > optind+3 || optind+1 >= argc) {
1103                 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
1104                 goto quit;
1105             }
1106
1107             suspend = pa_parse_boolean(argv[argc-1]);
1108
1109             if (argc > optind+2)
1110                 source_name = pa_xstrdup(argv[optind+1]);
1111         } else if (pa_streq(argv[optind], "set-card-profile")) {
1112             action = SET_CARD_PROFILE;
1113
1114             if (argc != optind+3) {
1115                 pa_log(_("You have to specify a card name/index and a profile name"));
1116                 goto quit;
1117             }
1118
1119             card_name = pa_xstrdup(argv[optind+1]);
1120             profile_name = pa_xstrdup(argv[optind+2]);
1121
1122         } else if (pa_streq(argv[optind], "set-sink-port")) {
1123             action = SET_SINK_PORT;
1124
1125             if (argc != optind+3) {
1126                 pa_log(_("You have to specify a sink name/index and a port name"));
1127                 goto quit;
1128             }
1129
1130             sink_name = pa_xstrdup(argv[optind+1]);
1131             port_name = pa_xstrdup(argv[optind+2]);
1132
1133         } else if (pa_streq(argv[optind], "set-source-port")) {
1134             action = SET_SOURCE_PORT;
1135
1136             if (argc != optind+3) {
1137                 pa_log(_("You have to specify a source name/index and a port name"));
1138                 goto quit;
1139             }
1140
1141             source_name = pa_xstrdup(argv[optind+1]);
1142             port_name = pa_xstrdup(argv[optind+2]);
1143
1144         } else if (pa_streq(argv[optind], "set-sink-volume")) {
1145             uint32_t v;
1146             action = SET_SINK_VOLUME;
1147
1148             if (argc != optind+3) {
1149                 pa_log(_("You have to specify a sink name/index and a volume"));
1150                 goto quit;
1151             }
1152
1153             if (pa_atou(argv[optind+2], &v) < 0) {
1154                 pa_log(_("Invalid volume specification"));
1155                 goto quit;
1156             }
1157
1158             sink_name = pa_xstrdup(argv[optind+1]);
1159             volume = (pa_volume_t) v;
1160
1161         } else if (pa_streq(argv[optind], "set-source-volume")) {
1162             uint32_t v;
1163             action = SET_SOURCE_VOLUME;
1164
1165             if (argc != optind+3) {
1166                 pa_log(_("You have to specify a source name/index and a volume"));
1167                 goto quit;
1168             }
1169
1170             if (pa_atou(argv[optind+2], &v) < 0) {
1171                 pa_log(_("Invalid volume specification"));
1172                 goto quit;
1173             }
1174
1175             source_name = pa_xstrdup(argv[optind+1]);
1176             volume = (pa_volume_t) v;
1177
1178         } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
1179             uint32_t v;
1180             action = SET_SINK_INPUT_VOLUME;
1181
1182             if (argc != optind+3) {
1183                 pa_log(_("You have to specify a sink input index and a volume"));
1184                 goto quit;
1185             }
1186
1187             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1188                 pa_log(_("Invalid sink input index"));
1189                 goto quit;
1190             }
1191
1192             if (pa_atou(argv[optind+2], &v) < 0) {
1193                 pa_log(_("Invalid volume specification"));
1194                 goto quit;
1195             }
1196
1197             volume = (pa_volume_t) v;
1198
1199         } else if (pa_streq(argv[optind], "set-sink-mute")) {
1200             int b;
1201             action = SET_SINK_MUTE;
1202
1203             if (argc != optind+3) {
1204                 pa_log(_("You have to specify a sink name/index and a mute boolean"));
1205                 goto quit;
1206             }
1207
1208             if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1209                 pa_log(_("Invalid volume specification"));
1210                 goto quit;
1211             }
1212
1213             sink_name = pa_xstrdup(argv[optind+1]);
1214             mute = b;
1215
1216         } else if (pa_streq(argv[optind], "set-source-mute")) {
1217             int b;
1218             action = SET_SOURCE_MUTE;
1219
1220             if (argc != optind+3) {
1221                 pa_log(_("You have to specify a source name/index and a mute boolean"));
1222                 goto quit;
1223             }
1224
1225             if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1226                 pa_log(_("Invalid volume specification"));
1227                 goto quit;
1228             }
1229
1230             source_name = pa_xstrdup(argv[optind+1]);
1231             mute = b;
1232
1233         } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
1234             int b;
1235             action = SET_SINK_INPUT_MUTE;
1236
1237             if (argc != optind+3) {
1238                 pa_log(_("You have to specify a sink input index and a mute boolean"));
1239                 goto quit;
1240             }
1241
1242             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1243                 pa_log(_("Invalid sink input index specification"));
1244                 goto quit;
1245             }
1246
1247             if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1248                 pa_log(_("Invalid volume specification"));
1249                 goto quit;
1250             }
1251
1252             mute = b;
1253
1254         } else if (pa_streq(argv[optind], "help")) {
1255             help(bn);
1256             ret = 0;
1257             goto quit;
1258         }
1259     }
1260
1261     if (action == NONE) {
1262         pa_log(_("No valid command specified."));
1263         goto quit;
1264     }
1265
1266     if (!(m = pa_mainloop_new())) {
1267         pa_log(_("pa_mainloop_new() failed."));
1268         goto quit;
1269     }
1270
1271     mainloop_api = pa_mainloop_get_api(m);
1272
1273     pa_assert_se(pa_signal_init(mainloop_api) == 0);
1274     pa_signal_new(SIGINT, exit_signal_callback, NULL);
1275     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1276     pa_disable_sigpipe();
1277
1278     if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1279         pa_log(_("pa_context_new() failed."));
1280         goto quit;
1281     }
1282
1283     pa_context_set_state_callback(context, context_state_callback, NULL);
1284     if (pa_context_connect(context, server, 0, NULL) < 0) {
1285         pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1286         goto quit;
1287     }
1288
1289     if (pa_mainloop_run(m, &ret) < 0) {
1290         pa_log(_("pa_mainloop_run() failed."));
1291         goto quit;
1292     }
1293
1294 quit:
1295     if (sample_stream)
1296         pa_stream_unref(sample_stream);
1297
1298     if (context)
1299         pa_context_unref(context);
1300
1301     if (m) {
1302         pa_signal_done();
1303         pa_mainloop_free(m);
1304     }
1305
1306     pa_xfree(server);
1307     pa_xfree(sample_name);
1308     pa_xfree(sink_name);
1309     pa_xfree(source_name);
1310     pa_xfree(module_args);
1311     pa_xfree(card_name);
1312     pa_xfree(profile_name);
1313
1314     if (sndfile)
1315         sf_close(sndfile);
1316
1317     if (proplist)
1318         pa_proplist_free(proplist);
1319
1320     return ret;
1321 }