ry; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiffstats
path: root/src/providers/ldap/sdap_async_initgroups_ad.c
blob: 8f8f0a4cc635818dcc7f75f9da603ce2f55c820f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
/*
    SSSD

    Authors:
        Stephen Gallagher <sgallagh@redhat.com>

    Copyright (C) 2012 Red Hat

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "util/util.h"
#include "providers/ldap/sdap_async.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async_private.h"
#include "providers/ldap/sdap_idmap.h"
#include "providers/ad/ad_common.h"
#include "lib/idmap/sss_idmap.h"

struct sdap_ad_match_rule_initgr_state {
    struct tevent_context *ev;
    struct sdap_options *opts;
    struct sysdb_ctx *sysdb;
    struct sss_domain_info *domain;
    struct sdap_handle *sh;
    const char *name;
    const char *orig_dn;
    const char **attrs;
    int timeout;
    const char *base_filter;
    char *filter;

    size_t count;
    struct sysdb_attrs **groups;

    size_t base_iter;
    struct sdap_search_base **search_bases;
};

static errno_t
sdap_get_ad_match_rule_initgroups_next_base(struct tevent_req *req);

static void
sdap_get_ad_match_rule_initgroups_step(struct tevent_req *subreq);

struct tevent_req *
sdap_get_ad_match_rule_initgroups_send(TALLOC_CTX *mem_ctx,
                                       struct tevent_context *ev,
                                       struct sdap_options *opts,
                                       struct sysdb_ctx *sysdb,
                                       struct sss_domain_info *domain,
                                       struct sdap_handle *sh,
                                       const char *name,
                                       const char *orig_dn,
                                       int timeout)
{
    errno_t ret;
    struct tevent_req *req;
    struct sdap_ad_match_rule_initgr_state *state;
    const char **filter_members;
    char *sanitized_user_dn;

    req = tevent_req_create(mem_ctx, &state,
                            struct sdap_ad_match_rule_initgr_state);
    if (!req) return NULL;

    state->ev = ev;
    state->opts = opts;
    state->sysdb = sysdb;
    state->domain = domain;
    state->sh = sh;
    state->name = name;
    state->orig_dn = orig_dn;
    state->base_iter = 0;
    state->search_bases = opts->sdom->group_search_bases;

    /* Request all of the group attributes that we know
     * about, except for 'member' because that wastes a
     * lot of bandwidth here and we only really
     * care about a single member (the one we already
     * have).
     */
    filter_members = talloc_array(state, const char *, 2);
    if (!filter_members) {
        ret = ENOMEM;
        goto immediate;
    }
    filter_members[0] = opts->group_map[SDAP_AT_GROUP_MEMBER].name;
    filter_members[1] = NULL;

    ret = build_attrs_from_map(state, opts->group_map,
                               SDAP_OPTS_GROUP,
                               filter_members,
                               &state->attrs, NULL);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("Could not build attribute map: [%s]\n",
               strerror(ret)));
        goto immediate;
    }

    /* Sanitize the user DN in case we have special characters in DN */
    ret = sss_filter_sanitize(state, state->orig_dn, &sanitized_user_dn);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("Could not sanitize user DN: %s\n",
               strerror(ret)));
        goto immediate;
    }

    /* Craft a special filter according to
     * http://msdn.microsoft.com/en-us/library/windows/desktop/aa746475%28v=vs.85%29.aspx
     */
    state->base_filter =
            talloc_asprintf(state,
                            "(&(%s:%s:=%s)(objectClass=%s))",
                            state->opts->group_map[SDAP_AT_GROUP_MEMBER].name,
                            SDAP_MATCHING_RULE_IN_CHAIN,
                            sanitized_user_dn,
                            state->opts->group_map[SDAP_OC_GROUP].name);
    talloc_zfree(sanitized_user_dn);
    if (!state->base_filter) {
        ret = ENOMEM;
        goto immediate;
    }

    /* Start the loop through the search bases to get all of the
     * groups to which this user belongs.
     */
    ret = sdap_get_ad_match_rule_initgroups_next_base(req);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("sdap_get_ad_match_rule_members_next_base failed: [%s]\n",
               strerror(ret)));
        goto immediate;
    }

    return req;

immediate:
    tevent_req_error(req, ret);
    tevent_req_post(req, ev);
    return req;
}

static errno_t
sdap_get_ad_match_rule_initgroups_next_base(struct tevent_req *req)
{
    struct tevent_req *subreq;
    struct sdap_ad_match_rule_initgr_state *state;

    state = tevent_req_data(req, struct sdap_ad_match_rule_initgr_state);

    talloc_zfree(state->filter);
    state->filter = sdap_get_id_specific_filter(state,
                        state->base_filter,
                        state->search_bases[state->base_iter]->filter);
    if (!state->filter) {
        return ENOMEM;
    }

    DEBUG(SSSDBG_TRACE_FUNC,
          ("Searching for groups with base [%s]\n",
           state->search_bases[state->base_iter]->basedn));

    subreq = sdap_get_generic_send(
            state, state->ev, state->opts, state->sh,
            state->search_bases[state->base_iter]->basedn,
            state->search_bases[state->base_iter]->scope,
            state->filter, state->attrs,
            state->opts->group_map, SDAP_OPTS_GROUP,
            state->timeout, true);
    if (!subreq) {
        return ENOMEM;
    }

    tevent_req_set_callback(subreq,
                            sdap_get_ad_match_rule_initgroups_step,
                            req);

    return EOK;
}

static void
sdap_get_ad_match_rule_initgroups_step(struct tevent_req *subreq)
{
    errno_t ret;
    struct tevent_req *req =
            tevent_req_callback_data(subreq, struct tevent_req);
    struct sdap_ad_match_rule_initgr_state *state =
            tevent_req_data(req, struct sdap_ad_match_rule_initgr_state);
    size_t count, i;
    struct sysdb_attrs **groups;
    char **sysdb_grouplist;

    ret = sdap_get_generic_recv(subreq, state, &count, &groups);
    talloc_zfree(subreq);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("LDAP search failed: [%s]\n", strerror(ret)));
        goto error;
    }

    DEBUG(SSSDBG_TRACE_LIBS,
          ("Search for users returned %zu results\n", count));

    /* Add this batch of groups to the list */
    if (count > 0) {
        state->groups = talloc_realloc(state, state->groups,
                                      struct sysdb_attrs *,
                                      state->count + count + 1);
        if (!state->groups) {
            tevent_req_error(req, ENOMEM);
            return;
        }

        /* Copy the new groups into the list */
        for (i = 0; i < count; i++) {
            state->groups[state->count + i] =
                    talloc_steal(state->groups, groups[i]);
        }

        state->count += count;
        state->groups[state->count] = NULL;
    }

    /* Continue checking other search bases */
    state->base_iter++;
    if (state->search_bases[state->base_iter]) {
        /* There are more search bases to try */
        ret = sdap_get_ad_match_rule_initgroups_next_base(req);
        if (ret != EOK) {
            goto error;
        }
        return;
    }

    /* No more search bases. Save the groups. */

    if (state->count == 0) {
        DEBUG(SSSDBG_TRACE_LIBS,
              ("User is not a member of any group in the search bases\n"));
    }

    /* Get the current sysdb group list for this user
     * so we can update it.
     */
    ret = get_sysdb_grouplist(state, state->sysdb, state->domain,
                              state->name, &sysdb_grouplist);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("Could not get the list of groups for [%s] in the sysdb: "
               "[%s]\n",
               state->name, strerror(ret)));
        goto error;
    }

    /* The extensibleMatch search rule eliminates the need for
     * nested group searches, so we can just update the
     * memberships now.
     */
    ret = sdap_initgr_common_store(state->sysdb,
                                   state->domain,
                                   state->opts,
                                   state->name,
                                   SYSDB_MEMBER_USER,
                                   sysdb_grouplist,
                                   state->groups,
                                   state->count);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("Could not store groups for user [%s]: [%s]\n",
               state->name, strerror(ret)));
        goto error;
    }

    tevent_req_done(req);
    return;

error:
    tevent_req_error(req, ret);
}

errno_t
sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req)
{
    TEVENT_REQ_RETURN_ON_ERROR(req);
    return EOK;
}

struct sdap_get_ad_tokengroups_state {
    struct tevent_context *ev;
    struct sss_idmap_ctx *idmap_ctx;
    const char *username;

    char **sids;
    size_t num_sids;
};

static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq);

static struct tevent_req *
sdap_get_ad_tokengroups_send(TALLOC_CTX *mem_ctx,
                             struct tevent_context *ev,
                             struct sdap_options *opts,
                             struct sdap_handle *sh,
                             const char *name,
                             const char *orig_dn,
                             int timeout)
{
    struct sdap_get_ad_tokengroups_state *state = NULL;
    struct tevent_req *req = NULL;
    struct tevent_req *subreq = NULL;
    const char *attrs[] = {AD_TOKENGROUPS_ATTR, NULL};
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state,
                            struct sdap_get_ad_tokengroups_state);
    if (req == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
        return NULL;
    }

    state->idmap_ctx = opts->idmap_ctx->map;
    state->ev = ev;
    state->username = talloc_strdup(state, name);
    if (state->username == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    subreq = sdap_get_generic_send(state, state->ev, opts, sh, orig_dn,
                                   LDAP_SCOPE_BASE, NULL, attrs,
                                   NULL, 0, timeout, false);
    if (subreq == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    tevent_req_set_callback(subreq, sdap_get_ad_tokengroups_done, req);

    return req;

immediately:
    if (ret == EOK) {
        tevent_req_done(req);
    } else {
        tevent_req_error(req, ret);
    }
    tevent_req_post(req, ev);

    return req;
}

static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq)
{
    TALLOC_CTX *tmp_ctx = NULL;
    struct sdap_get_ad_tokengroups_state *state = NULL;
    struct tevent_req *req = NULL;
    struct sysdb_attrs **users = NULL;
    struct ldb_message_element *el = NULL;
    enum idmap_error_code err;
    char *sid_str = NULL;
    size_t num_users;
    size_t i;
    errno_t ret;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req, struct sdap_get_ad_tokengroups_state);

    ret = sdap_get_generic_recv(subreq, tmp_ctx, &num_users, &users);
    talloc_zfree(subreq);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("LDAP search failed: [%s]\n", strerror(ret)));
        goto done;
    }

    if (num_users != 1) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("More than one result on a base search!\n"));
        ret = EINVAL;
        goto done;
    }

    /* get the list of sids from tokengroups */
    ret = sysdb_attrs_get_el_ext(users[0], AD_TOKENGROUPS_ATTR, false, &el);
    if (ret == ENOENT) {
        DEBUG(SSSDBG_TRACE_LIBS, ("No tokenGroups entries for [%s]\n",
                                  state->username));

        state->sids = NULL;
        state->num_sids = 0;
        ret = EOK;
        goto done;
    } else if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not read tokenGroups attribute: "
                                     "[%s]\n", strerror(ret)));
        goto done;
    }

    state->num_sids = 0;
    state->sids = talloc_zero_array(state, char*, el->num_values);
    if (state->sids == NULL) {
        ret = ENOMEM;
        goto done;
    }

    /* convert binary sid to string */
    for (i = 0; i < el->num_values; i++) {
        err = sss_idmap_bin_sid_to_sid(state->idmap_ctx, el->values[i].data,
                                       el->values[i].length, &sid_str);
        if (err != IDMAP_SUCCESS) {
            DEBUG(SSSDBG_MINOR_FAILURE,
                  ("Could not convert binary SID to string: [%s]. Skipping\n",
                   idmap_error_string(err)));
            continue;
        }

        state->sids[i] = talloc_move(state->sids, &sid_str);
        state->num_sids++;
    }

    /* shrink array to final number of elements */
    state->sids = talloc_realloc(state, state->sids, char*, state->num_sids);
    if (state->sids == NULL) {
        ret = ENOMEM;
        goto done;
    }

    ret = EOK;

done:
    talloc_free(tmp_ctx);

    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
}

static errno_t sdap_get_ad_tokengroups_recv(TALLOC_CTX *mem_ctx,
                                            struct tevent_req *req,
                                            size_t *_num_sids,
                                            char ***_sids)
{
    struct sdap_get_ad_tokengroups_state *state = NULL;
    state = tevent_req_data(req, struct sdap_get_ad_tokengroups_state);

    TEVENT_REQ_RETURN_ON_ERROR(req);

    if (_num_sids != NULL) {
        *_num_sids = state->num_sids;
    }

    if (_sids != NULL) {
        *_sids = talloc_steal(mem_ctx, state->sids);
    }

    return EOK;
}

static errno_t
sdap_ad_tokengroups_update_members(TALLOC_CTX *mem_ctx,
                                   const char *username,
                                   struct sysdb_ctx *sysdb,
                                   struct sss_domain_info *domain,
                                   char **ldap_groups)
{
    TALLOC_CTX *tmp_ctx = NULL;
    char **sysdb_groups = NULL;
    char **add_groups = NULL;
    char **del_groups = NULL;
    errno_t ret;

    tmp_ctx = talloc_new(NULL);
    if (tmp_ctx == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
        return ENOMEM;
    }

    /* Get the current sysdb group list for this user so we can update it. */
    ret = get_sysdb_grouplist_dn(tmp_ctx, sysdb, domain,
                                 username, &sysdb_groups);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not get the list of groups for "
              "[%s] in the sysdb: [%s]\n", username, strerror(ret)));
        goto done;
    }

    /* Find the differences between the sysdb and LDAP lists.
     * Groups in the sysdb only must be removed. */
    ret = diff_string_lists(tmp_ctx, ldap_groups, sysdb_groups,
                            &add_groups, &del_groups, NULL);
    if (ret != EOK) {
        goto done;
    }

    DEBUG(SSSDBG_TRACE_LIBS, ("Updating memberships for [%s]\n", username));

    ret = sysdb_update_members_dn(domain->sysdb, domain, username,
                                  SYSDB_MEMBER_USER,
                                  (const char *const *) add_groups,
                                  (const char *const *) del_groups);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE, ("Membership update failed [%d]: %s\n",
                                     ret, strerror(ret)));
        goto done;
    }

done:
    talloc_free(tmp_ctx);
    return ret;
}

struct sdap_ad_resolve_sids_state {
    struct tevent_context *ev;
    struct sdap_id_ctx *id_ctx;
    struct sdap_id_conn_ctx *conn;
    struct sdap_options *opts;
    struct sss_domain_info *domain;
    char **sids;

    const char *current_sid;
    int index;
};

static errno_t sdap_ad_resolve_sids_step(struct tevent_req *req);
static void sdap_ad_resolve_sids_done(struct tevent_req *subreq);

static struct tevent_req *
sdap_ad_resolve_sids_send(TALLOC_CTX *mem_ctx,
                          struct tevent_context *ev,
                          struct sdap_id_ctx *id_ctx,
                          struct sdap_id_conn_ctx *conn,
                          struct sdap_options *opts,
                          struct sss_domain_info *domain,
                          char **sids)
{
    struct sdap_ad_resolve_sids_state *state = NULL;
    struct tevent_req *req = NULL;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state,
                            struct sdap_ad_resolve_sids_state);
    if (req == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
        return NULL;
    }

    state->ev = ev;
    state->id_ctx = id_ctx;
    state->conn = conn;
    state->opts = opts;
    state->domain = get_domains_head(domain);
    state->sids = sids;
    state->index = 0;

    if (state->sids == NULL) {
        ret = EOK;
        goto immediately;
    }

    ret = sdap_ad_resolve_sids_step(req);
    if (ret != EAGAIN) {
        goto immediately;
    }

    return req;

immediately:
    if (ret == EOK) {
        tevent_req_done(req);
    } else {
        tevent_req_error(req, ret);
    }
    tevent_req_post(req, ev);

    return req;
}

static errno_t sdap_ad_resolve_sids_step(struct tevent_req *req)
{
    struct sdap_ad_resolve_sids_state *state = NULL;
    struct tevent_req *subreq = NULL;
    struct sdap_domain *sdap_domain = NULL;
    struct sss_domain_info *domain = NULL;

    state = tevent_req_data(req, struct sdap_ad_resolve_sids_state);

    do {
        state->current_sid = state->sids[state->index];
        if (state->current_sid == NULL) {
            return EOK;
        }
        state->index++;

        domain = find_subdomain_by_sid(state->domain, state->current_sid);
        if (domain == NULL) {
            DEBUG(SSSDBG_MINOR_FAILURE, ("SID %s does not belong to any known "
                                         "domain\n", state->current_sid));
        }
    } while (domain == NULL);

    sdap_domain = sdap_domain_get(state->opts, domain);
    if (sdap_domain == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("SDAP domain does not exist?\n"));
        return ERR_INTERNAL;
    }

    subreq = groups_get_send(state, state->ev, state->id_ctx, sdap_domain,
                             state->conn, state->current_sid,
                             BE_FILTER_SECID, BE_ATTR_CORE, false);
    if (subreq == NULL) {
        return ENOMEM;
    }

    tevent_req_set_callback(subreq, sdap_ad_resolve_sids_done, req);

    return EAGAIN;
}

static void sdap_ad_resolve_sids_done(struct tevent_req *subreq)
{
    struct sdap_ad_resolve_sids_state *state = NULL;
    struct tevent_req *req = NULL;
    int dp_error;
    int sdap_error;
    errno_t ret;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req, struct sdap_ad_resolve_sids_state);

    ret = groups_get_recv(subreq, &dp_error, &sdap_error);
    talloc_zfree(subreq);
    if (ret != EOK || sdap_error != EOK || dp_error != DP_ERR_OK) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to resolve SID %s [dp_error: %d, "
              "sdap_error: %d, ret: %d]: %s\n", state->current_sid, dp_error,
              sdap_error, ret, strerror(ret)));
        goto done;
    }

    ret = sdap_ad_resolve_sids_step(req);
    if (ret == EAGAIN) {
        /* continue with next SID */
        return;
    }

done:
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
}

static errno_t sdap_ad_resolve_sids_recv(struct tevent_req *req)
{
    TEVENT_REQ_RETURN_ON_ERROR(req);

    return EOK;
}


struct sdap_ad_tokengroups_initgr_mapping_state {
    struct tevent_context *ev;
    struct sdap_options *opts;
    struct sdap_handle *sh;
    struct sdap_idmap_ctx *idmap_ctx;
    struct sysdb_ctx *sysdb;
    struct sss_domain_info *domain;
    const char *orig_dn;
    int timeout;
    const char *username;

    struct sdap_id_op *op;
};

static void
sdap_ad_tokengroups_initgr_mapping_connect_done(struct tevent_req *subreq);
static void sdap_ad_tokengroups_initgr_mapping_done(struct tevent_req *subreq);

static struct tevent_req *
sdap_ad_tokengroups_initgr_mapping_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct sdap_options *opts,
                                        struct sysdb_ctx *sysdb,
                                        struct sss_domain_info *domain,
                                        struct sdap_handle *sh,
                                        const char *name,
                                        const char *orig_dn,
                                        int timeout)
{
    struct sdap_ad_tokengroups_initgr_mapping_state *state = NULL;
    struct tevent_req *req = NULL;
    struct tevent_req *subreq = NULL;
    struct sdap_domain *sdom;
    struct ad_id_ctx *subdom_id_ctx;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state,
                            struct sdap_ad_tokengroups_initgr_mapping_state);
    if (req == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
        return NULL;
    }

    state->ev = ev;
    state->opts = opts;
    state->sh = sh;
    state->idmap_ctx = opts->idmap_ctx;
    state->sysdb = sysdb;
    state->domain = domain;
    state->timeout = timeout;
    state->orig_dn = orig_dn;
    state->username = talloc_strdup(state, name);
    if (state->username == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    sdom = sdap_domain_get(opts, domain);
    if (sdom == NULL || sdom->pvt == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("No ID ctx available for [%s].\n",
                                    domain->name));
        ret = EINVAL;
        goto immediately;
    }
    subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
    state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
    if (!state->op) {
        DEBUG(2, ("sdap_id_op_create failed\n"));
        ret = ENOMEM;
        goto immediately;
    }

    subreq = sdap_id_op_connect_send(state->op, state, &ret);
    if (subreq == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    tevent_req_set_callback(subreq,
                            sdap_ad_tokengroups_initgr_mapping_connect_done,
                            req);

    return req;

immediately:
    if (ret == EOK) {
        tevent_req_done(req);
    } else {
        tevent_req_error(req, ret);
    }
    tevent_req_post(req, ev);

    return req;
}

static void
sdap_ad_tokengroups_initgr_mapping_connect_done(struct tevent_req *subreq)
{
    struct sdap_ad_tokengroups_initgr_mapping_state *state = NULL;
    struct tevent_req *req = NULL;
    int ret;
    int dp_error = DP_ERR_FATAL;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req,
                            struct sdap_ad_tokengroups_initgr_mapping_state);


    ret = sdap_id_op_connect_recv(subreq, &dp_error);
    talloc_zfree(subreq);

    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    subreq = sdap_get_ad_tokengroups_send(state, state->ev, state->opts,
                                          sdap_id_op_handle(state->op),
                                          state->username,
                                          state->orig_dn, state->timeout);
    if (subreq == NULL) {
        tevent_req_error(req, ENOMEM);
        return;
    }

    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_mapping_done,
                            req);

    return;
}

static void sdap_ad_tokengroups_initgr_mapping_done(struct tevent_req *subreq)
{
    TALLOC_CTX *tmp_ctx = NULL;
    struct sdap_ad_tokengroups_initgr_mapping_state *state = NULL;
    struct tevent_req *req = NULL;
    struct sss_domain_info *domain = NULL;
    struct ldb_message *msg = NULL;
    const char *attrs[] = {SYSDB_NAME, NULL};
    const char *name = NULL;
    const char *sid = NULL;
    char **sids = NULL;
    size_t num_sids = 0;
    size_t i;
    time_t now;
    gid_t gid;
    char **groups = NULL;
    size_t num_groups;
    errno_t ret, sret;
    bool in_transaction = false;

    tmp_ctx = talloc_new(NULL);
    if (tmp_ctx == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
        ret = ENOMEM;
        goto done;
    }

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req, struct sdap_ad_tokengroups_initgr_mapping_state);

    ret = sdap_get_ad_tokengroups_recv(state, subreq, &num_sids, &sids);
    talloc_zfree(subreq);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to acquire tokengroups [%d]: %s\n",
                                    ret, strerror(ret)));
        goto done;
    }

    num_groups = 0;
    groups = talloc_zero_array(tmp_ctx, char*, num_sids + 1);
    if (groups == NULL) {
        ret = ENOMEM;
        goto done;
    }

    now = time(NULL);
    ret = sysdb_transaction_start(state->sysdb);
    if (ret != EOK) {
        goto done;
    }
    in_transaction = true;

    for (i = 0; i < num_sids; i++) {
        sid = sids[i];
        DEBUG(SSSDBG_TRACE_LIBS, ("Processing membership SID [%s]\n", sid));

        ret = sdap_idmap_sid_to_unix(state->idmap_ctx, sid, &gid);
        if (ret == ENOTSUP) {
            DEBUG(SSSDBG_TRACE_FUNC, ("Skipping built-in object.\n"));
            ret = EOK;
            continue;
        } else if (ret != EOK) {
            DEBUG(SSSDBG_MINOR_FAILURE, ("Could not convert SID to GID: [%s]. "
                                         "Skipping\n", strerror(ret)));
            continue;
        }

        domain = find_subdomain_by_sid(get_domains_head(state->domain), sid);
        if (domain == NULL) {
            DEBUG(SSSDBG_MINOR_FAILURE, ("Domain not found for SID %s\n", sid));
            continue;
        }

        DEBUG(SSSDBG_TRACE_LIBS, ("SID [%s] maps to GID [%"SPRIgid"]\n",
                                  sid, gid));

        /* Check whether this GID already exists in the sysdb */
        ret = sysdb_search_group_by_gid(tmp_ctx, domain->sysdb, domain,
                                        gid, attrs, &msg);
        if (ret == EOK) {
            name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
            if (name == NULL) {
                DEBUG(SSSDBG_MINOR_FAILURE,
                      ("Could not retrieve group name from sysdb\n"));
                ret = EINVAL;
                goto done;
            }
        } else if (ret == ENOENT) {
            /* This is a new group. For now, we will store it under the name
             * of its SID. When a direct lookup of the group or its GID occurs,
             * it will replace this temporary entry. */
            name = sid;
            ret = sysdb_add_incomplete_group(domain->sysdb, domain, name, gid,
                                             NULL, sid, false, now);
            if (ret != EOK) {
                DEBUG(SSSDBG_MINOR_FAILURE, ("Could not create incomplete "
                                             "group: [%s]\n", strerror(ret)));
                goto done;
            }
        } else {
            /* Unexpected error */
            DEBUG(SSSDBG_MINOR_FAILURE, ("Could not look up group in sysdb: "
                                         "[%s]\n", strerror(ret)));
            goto done;
        }

        groups[num_groups] = sysdb_group_strdn(tmp_ctx, domain->name, name);
        if (groups[num_groups] == NULL) {
            ret = ENOMEM;
            goto done;
        }
        num_groups++;
    }

    groups[num_groups] = NULL;

    ret = sdap_ad_tokengroups_update_members(state, state->username,
                                             state->sysdb, state->domain,
                                             groups);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE, ("Membership update failed [%d]: %s\n",
                                     ret, strerror(ret)));
        goto done;
    }

    ret = sysdb_transaction_commit(state->sysdb);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not commit transaction! [%s]\n",
                                    strerror(ret)));
        goto done;
    }
    in_transaction = false;

done:
    talloc_free(tmp_ctx);

    if (in_transaction) {
        sret = sysdb_transaction_cancel(state->sysdb);
        DEBUG(SSSDBG_FATAL_FAILURE, ("Could not cancel transaction! [%s]\n",
                                     strerror(sret)));
    }

    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
}

static int sdap_ad_tokengroups_initgr_mapping_recv(struct tevent_req *req)
{
    TEVENT_REQ_RETURN_ON_ERROR(req);

    return EOK;
}

struct sdap_ad_tokengroups_initgr_posix_state {
    struct tevent_context *ev;
    struct sdap_id_ctx *id_ctx;
    struct sdap_id_conn_ctx *conn;
    struct sdap_options *opts;
    struct sdap_handle *sh;
    struct sysdb_ctx *sysdb;
    struct sss_domain_info *domain;
    const char *orig_dn;
    int timeout;
    const char *username;

    struct sdap_id_op *op;
};

static void
sdap_ad_tokengroups_initgr_posix_tg_done(struct tevent_req *subreq);

static void
sdap_ad_tokengroups_initgr_posix_sids_connect_done(struct tevent_req *subreq);
static void
sdap_ad_tokengroups_initgr_posix_sids_done(struct tevent_req *subreq);

static struct tevent_req *
sdap_ad_tokengroups_initgr_posix_send(TALLOC_CTX *mem_ctx,
                                      struct tevent_context *ev,
                                      struct sdap_id_ctx *id_ctx,
                                      struct sdap_id_conn_ctx *conn,
                                      struct sdap_options *opts,
                                      struct sysdb_ctx *sysdb,
                                      struct sss_domain_info *domain,
                                      struct sdap_handle *sh,
                                      const char *name,
                                      const char *orig_dn,
                                      int timeout)
{
    struct sdap_ad_tokengroups_initgr_posix_state *state = NULL;
    struct tevent_req *req = NULL;
    struct tevent_req *subreq = NULL;
    struct sdap_domain *sdom;
    struct ad_id_ctx *subdom_id_ctx;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state,
                            struct sdap_ad_tokengroups_initgr_posix_state);
    if (req == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
        return NULL;
    }

    state->ev = ev;
    state->id_ctx = id_ctx;
    state->conn = conn;
    state->opts = opts;
    state->sh = sh;
    state->sysdb = sysdb;
    state->domain = domain;
    state->orig_dn = orig_dn;
    state->timeout = timeout;
    state->username = talloc_strdup(state, name);
    if (state->username == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    sdom = sdap_domain_get(opts, domain);
    if (sdom == NULL || sdom->pvt == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("No ID ctx available for [%s].\n",
                                    domain->name));
        ret = EINVAL;
        goto immediately;
    }
    subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
    state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
    if (!state->op) {
        DEBUG(2, ("sdap_id_op_create failed\n"));
        ret = ENOMEM;
        goto immediately;
    }

    subreq = sdap_id_op_connect_send(state->op, state, &ret);
    if (subreq == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    tevent_req_set_callback(subreq,
                            sdap_ad_tokengroups_initgr_posix_sids_connect_done,
                            req);

    return req;

immediately:
    if (ret == EOK) {
        tevent_req_done(req);
    } else {
        tevent_req_error(req, ret);
    }
    tevent_req_post(req, ev);

    return req;
}

static void
sdap_ad_tokengroups_initgr_posix_sids_connect_done(struct tevent_req *subreq)
{
    struct sdap_ad_tokengroups_initgr_posix_state *state = NULL;
    struct tevent_req *req = NULL;
    int ret;
    int dp_error = DP_ERR_FATAL;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req,
                            struct sdap_ad_tokengroups_initgr_posix_state);


    ret = sdap_id_op_connect_recv(subreq, &dp_error);
    talloc_zfree(subreq);

    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    subreq = sdap_get_ad_tokengroups_send(state, state->ev, state->opts,
                                          sdap_id_op_handle(state->op),
                                          state->username, state->orig_dn,
                                          state->timeout);
    if (subreq == NULL) {
        tevent_req_error(req, ENOMEM);
        return;
    }

    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_posix_tg_done,
                            req);

    return;
}

static void
sdap_ad_tokengroups_initgr_posix_tg_done(struct tevent_req *subreq)
{
    TALLOC_CTX *tmp_ctx = NULL;
    struct sdap_ad_tokengroups_initgr_posix_state *state = NULL;
    struct tevent_req *req = NULL;
    struct sss_domain_info *domain = NULL;
    struct ldb_message *msg = NULL;
    const char *attrs[] = {SYSDB_NAME, SYSDB_POSIX, NULL};
    const char *is_posix = NULL;
    const char *name = NULL;
    char *sid = NULL;
    char **sids = NULL;
    size_t num_sids = 0;
    char **valid_groups = NULL;
    size_t num_valid_groups;
    char **missing_sids = NULL;
    size_t num_missing_sids;
    size_t i;
    errno_t ret;

    tmp_ctx = talloc_new(NULL);
    if (tmp_ctx == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
        ret = ENOMEM;
        goto done;
    }

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req,
                            struct sdap_ad_tokengroups_initgr_posix_state);

    ret = sdap_get_ad_tokengroups_recv(state, subreq, &num_sids, &sids);
    talloc_zfree(subreq);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to acquire tokengroups [%d]: %s\n",
                                    ret, strerror(ret)));
        goto done;
    }

    num_valid_groups = 0;
    valid_groups = talloc_zero_array(tmp_ctx, char*, num_sids + 1);
    if (valid_groups == NULL) {
        ret = ENOMEM;
        goto done;
    }

    num_missing_sids = 0;
    missing_sids = talloc_zero_array(tmp_ctx, char*, num_sids + 1);
    if (missing_sids == NULL) {
        ret = ENOMEM;
        goto done;
    }

    /* For each SID check if it is already present in the cache. If yes, we
     * will get name of the group and update the membership. Otherwise we need
     * to remember the SID and download missing groups one by one. */
    for (i = 0; i < num_sids; i++) {
        sid = sids[i];
        DEBUG(SSSDBG_TRACE_LIBS, ("Processing membership SID [%s]\n", sid));

        domain = find_subdomain_by_sid(get_domains_head(state->domain), sid);
        if (domain == NULL) {
            DEBUG(SSSDBG_MINOR_FAILURE, ("Domain not found for SID %s\n", sid));
            continue;
        }

        ret = sysdb_search_group_by_sid_str(tmp_ctx, domain->sysdb, domain,
                                            sid, attrs, &msg);
        if (ret == EOK) {
            is_posix = ldb_msg_find_attr_as_string(msg, SYSDB_POSIX, NULL);
            if (is_posix != NULL && strcmp(is_posix, "FALSE") == 0) {
                /* skip non-posix group */
                continue;
            }

            /* we will update membership of this group */
            name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
            if (name == NULL) {
                DEBUG(SSSDBG_MINOR_FAILURE,
                      ("Could not retrieve group name from sysdb\n"));
                ret = EINVAL;
                goto done;
            }

            valid_groups[num_valid_groups] = sysdb_group_strdn(tmp_ctx,
                                                               domain->name,
                                                               name);
            if (valid_groups[num_valid_groups] == NULL) {
                ret = ENOMEM;
                goto done;
            }
            num_valid_groups++;
        } else if (ret == ENOENT) {
            /* we need to download this group */
            missing_sids[num_missing_sids] = talloc_steal(missing_sids, sid);
            num_missing_sids++;

            DEBUG(SSSDBG_TRACE_FUNC, ("Missing SID %s will be downloaded\n",
                                      sid));
        } else {
            DEBUG(SSSDBG_MINOR_FAILURE, ("Could not look up group in sysdb: "
                                         "[%s]\n", strerror(ret)));
            goto done;
        }
    }

    valid_groups[num_valid_groups] = NULL;
    missing_sids[num_missing_sids] = NULL;

    /* update membership of existing groups */
    ret = sdap_ad_tokengroups_update_members(state, state->username,
                                             state->sysdb, state->domain,
                                             valid_groups);
    if (ret != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE, ("Membership update failed [%d]: %s\n",
                                     ret, strerror(ret)));
        goto done;
    }

    /* download missing SIDs */
    missing_sids = talloc_steal(state, missing_sids);
    subreq = sdap_ad_resolve_sids_send(state, state->ev, state->id_ctx,
                                       state->conn,
                                       state->opts, state->domain,
                                       missing_sids);
    if (subreq == NULL) {
        ret = ENOMEM;
        goto done;
    }

    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_posix_sids_done,
                            req);

    return;

done:
    talloc_free(tmp_ctx);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
}

static void
sdap_ad_tokengroups_initgr_posix_sids_done(struct tevent_req *subreq)
{
    struct tevent_req *req = NULL;
    errno_t ret;

    req = tevent_req_callback_data(subreq, struct tevent_req);

    ret = sdap_ad_resolve_sids_recv(subreq);
    talloc_zfree(subreq);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to resolve missing SIDs "
                                    "[%d]: %s\n", ret, strerror(ret)));
        goto done;
    }

done:
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
}

static errno_t sdap_ad_tokengroups_initgr_posix_recv(struct tevent_req *req)
{
    TEVENT_REQ_RETURN_ON_ERROR(req);

    return EOK;
}

struct sdap_ad_tokengroups_initgroups_state {
    bool use_id_mapping;
    struct sss_domain_info *domain;
};

static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq);

struct tevent_req *
sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
                                    struct tevent_context *ev,
                                    struct sdap_id_ctx *id_ctx,
                                    struct sdap_id_conn_ctx *conn,
                                    struct sdap_options *opts,
                                    struct sysdb_ctx *sysdb,
                                    struct sss_domain_info *domain,
                                    struct sdap_handle *sh,
                                    const char *name,
                                    const char *orig_dn,
                                    int timeout,
                                    bool use_id_mapping)
{
    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
    struct tevent_req *req = NULL;
    struct tevent_req *subreq = NULL;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state,
                            struct sdap_ad_tokengroups_initgroups_state);
    if (req == NULL) {
        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
        return NULL;
    }

    state->use_id_mapping = use_id_mapping;
    state->domain = domain;

    if (state->use_id_mapping && !IS_SUBDOMAIN(state->domain)) {
        subreq = sdap_ad_tokengroups_initgr_mapping_send(state, ev, opts,
                                                         sysdb, domain, sh,
                                                         name, orig_dn,
                                                         timeout);
    } else {
        subreq = sdap_ad_tokengroups_initgr_posix_send(state, ev, id_ctx, conn,
                                                       opts, sysdb, domain, sh,
                                                       name, orig_dn,
                                                       timeout);
    }
    if (subreq == NULL) {
        ret = ENOMEM;
        goto immediately;
    }

    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgroups_done, req);

    return req;

immediately:
    if (ret == EOK) {
        tevent_req_done(req);
    } else {
        tevent_req_error(req, ret);
    }
    tevent_req_post(req, ev);

    return req;
}

static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq)
{
    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
    struct tevent_req *req = NULL;
    errno_t ret;

    req = tevent_req_callback_data(subreq, struct tevent_req);
    state = tevent_req_data(req, struct sdap_ad_tokengroups_initgroups_state);

    if (state->use_id_mapping && !IS_SUBDOMAIN(state->domain)) {
        ret = sdap_ad_tokengroups_initgr_mapping_recv(subreq);
    } else {
        ret = sdap_ad_tokengroups_initgr_posix_recv(subreq);
    }
    talloc_zfree(subreq);
    if (ret != EOK) {
        goto done;
    }

done:
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
}

errno_t sdap_ad_tokengroups_initgroups_recv(struct tevent_req *req)
{
    TEVENT_REQ_RETURN_ON_ERROR(req);

    return EOK;
}
wb">int brick_list_len = 0; char *tmp_host = NULL; GF_ASSERT (words); GF_ASSERT (wordcount); GF_ASSERT (bricks); GF_ASSERT (brick_index > 0); GF_ASSERT (brick_index < wordcount); strncpy (brick_list, space, strlen (space)); brick_list_len++; while (brick_index < wordcount) { if (validate_brick_name ((char *)words[brick_index])) { cli_err ("Wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[brick_index]); ret = -1; goto out; } else { delimiter = strrchr (words[brick_index], ':'); ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } if ((brick_list_len + strlen (words[brick_index]) + 1) > sizeof (brick_list)) { cli_err ("Total brick list is larger than a request. " "Can take (brick_count %d)", *brick_count); ret = -1; goto out; } tmp_host = gf_strdup ((char *)words[brick_index]); if (!tmp_host) { gf_log ("cli", GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } get_host_name (tmp_host, &host_name); if (!host_name) { ret = -1; gf_log("cli",GF_LOG_ERROR, "Unable to allocate " "memory"); goto out; } if (!(strcmp (host_name, "localhost") && strcmp (host_name, "127.0.0.1") && strncmp (host_name, "0.", 2))) { cli_err ("Please provide a valid hostname/ip other " "than localhost, 127.0.0.1 or loopback " "address (0.0.0.0 to 0.255.255.255)."); ret = -1; GF_FREE (tmp_host); goto out; } if (!valid_internet_address (host_name, _gf_false)) { cli_err ("internet address '%s' does not conform to " "standards", host_name); } GF_FREE (tmp_host); tmp_list = gf_strdup (brick_list + 1); if (free_list_ptr) { GF_FREE (free_list_ptr); free_list_ptr = NULL; } free_list_ptr = tmp_list; j = 0; while(j < *brick_count) { strtok_r (tmp_list, " ", &tmpptr); if (!(strcmp (tmp_list, words[brick_index]))) { ret = -1; cli_err ("Found duplicate" " exports %s",words[brick_index]); goto out; } tmp_list = tmpptr; j++; } strcat (brick_list, words[brick_index]); strcat (brick_list, " "); brick_list_len += (strlen (words[brick_index]) + 1); ++(*brick_count); ++brick_index; } *bricks = gf_strdup (brick_list); if (!*bricks) ret = -1; out: GF_FREE (free_list_ptr); return ret; } int32_t cli_cmd_create_disperse_check (struct cli_state *state, int *disperse, int *redundancy, int *data, int count) { int i = 0; int tmp = 0; gf_answer_t answer = GF_ANSWER_NO; char question[128]; const char *question1 = "There isn't an optimal redundancy value " "for this configuration. Do you want to " "create the volume with redundancy 1 ?"; const char *question2 = "The optimal redundancy for this " "configuration is %d. Do you want to create " "the volume with this value ?"; const char *question3 = "This configuration is not optimal on most " "workloads. Do you want to use it ?"; const char *question4 = "Redundancy for this configuration is %d. " "Do you want to create " "the volume with this value ?"; if (*data > 0) { if (*disperse > 0 && *redundancy > 0) { if (*disperse != (*data + *redundancy)) { cli_err ("Disperse count(%d) should be equal " "to sum of disperse-data count(%d) and " "redundancy count(%d)", *disperse, *data, *redundancy); return -1; } } else if (*redundancy > 0) { *disperse = *data + *redundancy; } else if (*disperse > 0) { *redundancy = *disperse - *data; } else { if ((count - *data) >= *data) { cli_err ("Please provide redundancy count " "along with disperse-data count"); return -1; } else { sprintf (question, question4, count - *data); answer = cli_cmd_get_confirmation (state, question); if (answer == GF_ANSWER_NO) return -1; *redundancy = count - *data; *disperse = count; } } } if (*disperse <= 0) { if (count < 3) { cli_err ("number of bricks must be greater " "than 2"); return -1; } *disperse = count; } if (*redundancy == -1) { tmp = *disperse - 1; for (i = tmp / 2; (i > 0) && ((tmp & -tmp) != tmp); i--, tmp--); if (i == 0) { answer = cli_cmd_get_confirmation(state, question1); if (answer == GF_ANSWER_NO) return -1; *redundancy = 1; } else { *redundancy = *disperse - tmp; if (*redundancy > 1) { sprintf(question, question2, *redundancy); answer = cli_cmd_get_confirmation(state, question); if (answer == GF_ANSWER_NO) return -1; } } tmp = 0; } else { tmp = *disperse - *redundancy; } if (*redundancy > (*disperse - 1) / 2) { cli_err ("redundancy must be less than %d for a " "disperse %d volume", (*disperse + 1) / 2, *disperse); return -1; } if ((tmp & -tmp) != tmp) { answer = cli_cmd_get_confirmation(state, question3); if (answer == GF_ANSWER_NO) return -1; } return 0; } static int32_t cli_validate_disperse_volume (char *word, gf1_cluster_type type, const char **words, int32_t wordcount, int32_t index, int32_t *disperse_count, int32_t *redundancy_count, int32_t *data_count) { int ret = -1; switch (type) { case GF_CLUSTER_TYPE_NONE: case GF_CLUSTER_TYPE_DISPERSE: if (strcmp (word, "disperse") == 0) { if (*disperse_count >= 0) { cli_err ("disperse option given twice"); goto out; } if (wordcount < (index+2)) { goto out; } ret = gf_string2int (words[index + 1], disperse_count); if (ret == -1 && errno == EINVAL) { *disperse_count = 0; ret = 1; } else if (ret == -1) { goto out; } else { if (*disperse_count < 3) { cli_err ("disperse count must " "be greater than 2"); goto out; } ret = 2; } } else if (strcmp (word, "disperse-data") == 0) { if (*data_count >= 0) { cli_err ("disperse-data option given twice"); goto out; } if (wordcount < (index+2)) { goto out; } ret = gf_string2int (words[index+1], data_count); if (ret == -1 || *data_count < 2) { cli_err ("disperse-data must be greater than 1"); goto out; } ret = 2; } else if (strcmp (word, "redundancy") == 0) { if (*redundancy_count >= 0) { cli_err ("redundancy option given twice"); goto out; } if (wordcount < (index+2)) { goto out; } ret = gf_string2int (words[index+1], redundancy_count); if (ret == -1 || *redundancy_count < 1) { cli_err ("redundancy must be greater than 0"); goto out; } ret = 2; } break; case GF_CLUSTER_TYPE_STRIPE_REPLICATE: cli_err ("striped-replicated-dispersed volume " "is not supported"); goto out; case GF_CLUSTER_TYPE_TIER: cli_err ("tier-dispersed volume is not " "supported"); goto out; case GF_CLUSTER_TYPE_STRIPE: cli_err ("striped-dispersed volume is not " "supported"); goto out; case GF_CLUSTER_TYPE_REPLICATE: cli_err ("replicated-dispersed volume is not " "supported"); goto out; default: cli_err ("Invalid type given"); break; } out: return ret; } int32_t cli_validate_volname (const char *volname) { int32_t ret = -1; int32_t i = -1; static const char * const invalid_volnames[] = { "volume", "type", "subvolumes", "option", "end-volume", "all", "volume_not_in_ring", "description", "force", "snap-max-hard-limit", "snap-max-soft-limit", "auto-delete", "activate-on-create", NULL}; if (volname[0] == '-') goto out; for (i = 0; invalid_volnames[i]; i++) { if (!strcmp (volname, invalid_volnames[i])) { cli_err ("\"%s\" cannot be the name of a volume.", volname); goto out; } } if (strchr (volname, '/')) goto out; if (strlen (volname) > GD_VOLUME_NAME_MAX) { cli_err("Volume name exceeds %d characters.", GD_VOLUME_NAME_MAX); goto out; } for (i = 0; i < strlen (volname); i++) { if (!isalnum (volname[i]) && (volname[i] != '_') && (volname[i] != '-')) { cli_err ("Volume name should not contain \"%c\"" " character.\nVolume names can only" "contain alphanumeric, '-' and '_' " "characters.", volname[i]); goto out; } } ret = 0; out: return ret; } int32_t cli_cmd_volume_create_parse (struct cli_state *state, const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; gf1_cluster_type type = GF_CLUSTER_TYPE_NONE; int count = 1; int sub_count = 1; int brick_index = 0; char *trans_type = NULL; int32_t index = 0; char *bricks = NULL; int32_t brick_count = 0; char *opwords[] = { "replica", "stripe", "transport", "disperse", "redundancy", "disperse-data", "arbiter", NULL }; char *w = NULL; char *ptr = NULL; int op_count = 0; int32_t replica_count = 1; int32_t arbiter_count = 0; int32_t stripe_count = 1; int32_t disperse_count = -1; int32_t redundancy_count = -1; int32_t disperse_data_count = -1; gf_boolean_t is_force = _gf_false; int wc = wordcount; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (wordcount < 3) goto out; volname = (char *)words[2]; GF_ASSERT (volname); /* Validate the volume name here itself */ if (cli_validate_volname (volname) < 0) goto out; if (wordcount < 4) { ret = -1; goto out; } type = GF_CLUSTER_TYPE_NONE; index = 3; while (op_count < 3) { ret = -1; w = str_getunamb (words[index], opwords); if (!w) { break; } else if ((strcmp (w, "replica")) == 0) { switch (type) { case GF_CLUSTER_TYPE_STRIPE_REPLICATE: case GF_CLUSTER_TYPE_REPLICATE: cli_err ("replica option given twice"); goto out; case GF_CLUSTER_TYPE_NONE: type = GF_CLUSTER_TYPE_REPLICATE; break; case GF_CLUSTER_TYPE_STRIPE: type = GF_CLUSTER_TYPE_STRIPE_REPLICATE; break; case GF_CLUSTER_TYPE_TIER: cli_err ("replicated-tiered volume is not " "supported"); goto out; break; case GF_CLUSTER_TYPE_DISPERSE: cli_err ("replicated-dispersed volume is not " "supported"); goto out; default: cli_err ("Invalid type given"); goto out; } if (wordcount < (index+2)) { ret = -1; goto out; } replica_count = strtol (words[index+1], NULL, 0); if (replica_count < 2) { cli_err ("replica count should be greater" " than 1"); ret = -1; goto out; } ret = dict_set_int32 (dict, "replica-count", replica_count); if (ret) goto out; index += 2; if (words[index]) { if (!strcmp (words[index], "arbiter")) { ret = gf_string2int (words[index+1], &arbiter_count); if (ret == -1 || arbiter_count != 1 || replica_count != 3) { cli_err ("For arbiter " "configuration, " "replica count must be" " 3 and arbiter count " "must be 1. The 3rd " "brick of the replica " "will be the arbiter"); ret = -1; goto out; } ret = dict_set_int32 (dict, "arbiter-count", arbiter_count); if (ret) goto out; index += 2; } } } else if ((strcmp (w, "stripe")) == 0) { switch (type) { case GF_CLUSTER_TYPE_STRIPE_REPLICATE: case GF_CLUSTER_TYPE_STRIPE: cli_err ("stripe option given twice"); goto out; case GF_CLUSTER_TYPE_NONE: type = GF_CLUSTER_TYPE_STRIPE; break; case GF_CLUSTER_TYPE_REPLICATE: type = GF_CLUSTER_TYPE_STRIPE_REPLICATE; break; case GF_CLUSTER_TYPE_DISPERSE: cli_err ("striped-dispersed volume is not " "supported"); goto out; case GF_CLUSTER_TYPE_TIER: cli_err ("striped-tier volume is not " "supported"); goto out; default: cli_err ("Invalid type given"); goto out; } if (wordcount < (index + 2)) { ret = -1; goto out; } stripe_count = strtol (words[index+1], NULL, 0); if (stripe_count < 2) { cli_err ("stripe count should be greater" " than 1"); ret = -1; goto out; } ret = dict_set_int32 (dict, "stripe-count", stripe_count); if (ret) goto out; index += 2; } else if ((strcmp (w, "transport")) == 0) { if (trans_type) { cli_err ("'transport' option given more" " than one time"); goto out; } if ((strcasecmp (words[index+1], "tcp") == 0)) { trans_type = gf_strdup ("tcp"); } else if ((strcasecmp (words[index+1], "rdma") == 0)) { trans_type = gf_strdup ("rdma"); } else if ((strcasecmp (words[index+1], "tcp,rdma") == 0) || (strcasecmp (words[index+1], "rdma,tcp") == 0)) { trans_type = gf_strdup ("tcp,rdma"); } else { gf_log ("", GF_LOG_ERROR, "incorrect transport" " protocol specified"); ret = -1; goto out; } index += 2; } else if ((strcmp (w, "disperse") == 0) || (strcmp (w, "redundancy") == 0) || (strcmp (w, "disperse-data") == 0)) { ret = cli_validate_disperse_volume (w, type, words, wordcount, index, &disperse_count, &redundancy_count, &disperse_data_count); if (ret < 0) goto out; index += ret; type = GF_CLUSTER_TYPE_DISPERSE; } else { GF_ASSERT (!"opword mismatch"); ret = -1; goto out; } op_count++; } if (!trans_type) trans_type = gf_strdup ("tcp"); /* reset the count value now */ count = 1; if (index >= wordcount) { ret = -1; goto out; } brick_index = index; if (strcmp (words[wordcount - 1], "force") == 0) { is_force = _gf_true; wc = wordcount - 1; } ret = cli_cmd_bricks_parse (words, wc, brick_index, &bricks, &brick_count); if (ret) goto out; /* If brick-count is not valid when replica or stripe is given, exit here */ if (!brick_count) { cli_err ("No bricks specified"); ret = -1; goto out; } if (type == GF_CLUSTER_TYPE_DISPERSE) { ret = cli_cmd_create_disperse_check (state, &disperse_count, &redundancy_count, &disperse_data_count, brick_count); if (!ret) ret = dict_set_int32 (dict, "disperse-count", disperse_count); if (!ret) ret = dict_set_int32 (dict, "redundancy-count", redundancy_count); if (ret) goto out; sub_count = disperse_count; } else sub_count = stripe_count * replica_count; if (brick_count % sub_count) { if (type == GF_CLUSTER_TYPE_STRIPE) cli_err ("number of bricks is not a multiple of " "stripe count"); else if (type == GF_CLUSTER_TYPE_REPLICATE) cli_err ("number of bricks is not a multiple of " "replica count"); else if (type == GF_CLUSTER_TYPE_DISPERSE) cli_err ("number of bricks is not a multiple of " "disperse count"); else cli_err ("number of bricks given doesn't match " "required count"); ret = -1; goto out; } /* Everything is parsed fine. start setting info in dict */ ret = dict_set_str (dict, "volname", volname); if (ret) goto out; ret = dict_set_int32 (dict, "type", type); if (ret) goto out; ret = dict_set_dynstr (dict, "transport", trans_type); if (ret) goto out; trans_type = NULL; ret = dict_set_dynstr (dict, "bricks", bricks); if (ret) goto out; ret = dict_set_int32 (dict, "count", brick_count); if (ret) goto out; ret = dict_set_int32 (dict, "force", is_force); if (ret) goto out; *options = dict; out: if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to parse create volume CLI"); if (dict) dict_destroy (dict); } GF_FREE (trans_type); return ret; } int32_t cli_cmd_volume_reset_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (wordcount < 3) goto out; if (wordcount > 5) goto out; volname = (char *)words[2]; if (!volname) { ret = -1; goto out; } ret = dict_set_str (dict, "volname", volname); if (ret) goto out; if (wordcount == 3) { ret = dict_set_str (dict, "key", "all"); if (ret) goto out; } if (wordcount >= 4) { if (!strcmp ("force", (char*)words[3])) { ret = dict_set_int32 (dict, "force", 1); if (ret) goto out; ret = dict_set_str (dict, "key", "all"); if (ret) goto out; } else { ret = dict_set_str (dict, "key", (char *)words[3]); if (ret) goto out; } } if (wordcount == 5) { if (strcmp ("force", (char*)words[4])) { ret = -1; goto out; } else { ret = dict_set_int32 (dict, "force", 1); if (ret) goto out; } } *options = dict; out: if (ret && dict) { dict_destroy (dict); } return ret; } /* Parsing global option for NFS-Ganesha config * gluster nfs-ganesha enable/disable */ int32_t cli_cmd_ganesha_parse (struct cli_state *state, const char **words, int wordcount, dict_t **options, char **op_errstr) { dict_t *dict = NULL; int ret = -1; int flags = 0; char *key = NULL; char *value = NULL; int i = 0; char *w = NULL; char *opwords[] = { "enable", "disable", NULL }; const char *question = NULL; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (wordcount != 2) goto out; key = (char *) words[0]; value = (char *) words[1]; if (!key || !value) { cli_out ("Usage : nfs-ganesha <enable/disable>"); ret = -1; goto out; } ret = gf_strip_whitespace (value, strlen (value)); if (ret == -1) goto out; if (strcmp (key, "nfs-ganesha")) { gf_asprintf (op_errstr, "Global option: error: ' %s '" "is not a valid global option.", key); ret = -1; goto out; } w = str_getunamb (value, opwords); if (!w) { cli_out ("Invalid global option \n" "Usage : nfs-ganesha <enable/disable>"); ret = -1; goto out; } question = "Enabling NFS-Ganesha requires Gluster-NFS to be" " disabled across the trusted pool. Do you " "still want to continue?\n"; if (strcmp (value, "enable") == 0) { answer = cli_cmd_get_confirmation (state, question); if (GF_ANSWER_NO == answer) { gf_log ("cli", GF_LOG_ERROR, "Global operation " "cancelled, exiting"); ret = -1; goto out; } } cli_out ("This will take a few minutes to complete. Please wait .."); ret = dict_set_str (dict, "key", key); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "dict set on key failed"); goto out; } ret = dict_set_str (dict, "value", value); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "dict set on value failed"); goto out; } ret = dict_set_str (dict, "globalname", "All"); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "dict set on global" " key failed."); goto out; } ret = dict_set_int32 (dict, "hold_global_locks", _gf_true); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "dict set on global key " "failed."); goto out; } *options = dict; out: if (ret) dict_unref (dict); return ret; } int32_t cli_cmd_inode_quota_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) { gf_log ("cli", GF_LOG_ERROR, "dict_new failed"); goto out; } if (wordcount != 4) goto out; volname = (char *)words[2]; if (!volname) { ret = -1; goto out; } /* Validate the volume name here itself */ if (cli_validate_volname (volname) < 0) goto out; ret = dict_set_str (dict, "volname", volname); if (ret < 0) goto out; if (strcmp (words[3], "enable") != 0) { cli_out ("Invalid quota option : %s", words[3]); ret = -1; goto out; } ret = dict_set_int32 (dict, "type", GF_QUOTA_OPTION_TYPE_ENABLE_OBJECTS); if (ret < 0) goto out; *options = dict; out: if (ret < 0) { if (dict) dict_destroy (dict); } return ret; } int32_t cli_cmd_quota_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; int i = -1; char key[20] = {0, }; int64_t value = 0; gf_quota_type type = GF_QUOTA_OPTION_TYPE_NONE; char *opwords[] = { "enable", "disable", "limit-usage", "remove", "list", "alert-time", "soft-timeout", "hard-timeout", "default-soft-limit", "limit-objects", "list-objects", "remove-objects", NULL}; char *w = NULL; uint32_t time = 0; double percent = 0; char *end_ptr = NULL; int64_t limit = 0; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) { gf_log ("cli", GF_LOG_ERROR, "dict_new failed"); goto out; } if (wordcount < 4) goto out; volname = (char *)words[2]; if (!volname) { ret = -1; goto out; } /* Validate the volume name here itself */ if (cli_validate_volname (volname) < 0) goto out; ret = dict_set_str (dict, "volname", volname); if (ret < 0) goto out; w = str_getunamb (words[3], opwords); if (!w) { cli_out ("Invalid quota option : %s", words[3]); ret = - 1; goto out; } if (strcmp (w, "enable") == 0) { if (wordcount == 4) { type = GF_QUOTA_OPTION_TYPE_ENABLE; ret = 0; goto set_type; } else { ret = -1; goto out; } } if (strcmp (w, "disable") == 0) { if (wordcount == 4) { type = GF_QUOTA_OPTION_TYPE_DISABLE; ret = 0; goto set_type; } else { ret = -1; goto out; } } if (strcmp (w, "limit-usage") == 0) { type = GF_QUOTA_OPTION_TYPE_LIMIT_USAGE; } else if (strcmp (w, "limit-objects") == 0) { type = GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS; } if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE || type == GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS) { if (wordcount < 6 || wordcount > 7) { ret = -1; goto out; } if (words[4][0] != '/') { cli_err ("Please enter absolute path"); ret = -1; goto out; } ret = dict_set_str (dict, "path", (char *) words[4]); if (ret) goto out; if (!words[5]) { cli_err ("Please enter the limit value to be set"); ret = -1; goto out; } if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE) { ret = gf_string2bytesize_int64 (words[5], &value); if (ret != 0 || value < 0) { if (errno == ERANGE || value < 0) cli_err ("Value out of range " "(0 - %"PRId64 "): %s", INT64_MAX, words[5]); else cli_err ("Please enter a correct " "value"); goto out; } } else { errno = 0; limit = strtol (words[5], &end_ptr, 10); if (errno == ERANGE || errno == EINVAL || limit <= 0 || strcmp (end_ptr, "") != 0) { ret = -1; cli_err ("Please enter an interger value in " "the range 1 - %"PRId64, INT64_MAX); goto out; } } ret = dict_set_str (dict, "hard-limit", (char *) words[5]); if (ret < 0) goto out; if (wordcount == 7) { ret = gf_string2percent (words[6], &percent); if (ret != 0 || percent > 100) { ret = -1; cli_err ("Please enter a correct value " "in the range of 0 to 100"); goto out; } ret = dict_set_str (dict, "soft-limit", (char *) words[6]); if (ret < 0) goto out; } goto set_type; } if (strcmp (w, "remove") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_REMOVE; if (words[4][0] != '/') { cli_err ("Please enter absolute path"); ret = -1; goto out; } ret = dict_set_str (dict, "path", (char *) words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp (w, "remove-objects") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_REMOVE_OBJECTS; if (words[4][0] != '/') { cli_err ("Please enter absolute path"); ret = -1; goto out; } ret = dict_set_str (dict, "path", (char *) words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp (w, "list") == 0) { type = GF_QUOTA_OPTION_TYPE_LIST; if (words[4] && words[4][0] != '/') { cli_err ("Please enter absolute path"); ret = -1; goto out; } i = 4; while (i < wordcount) { snprintf (key, 20, "path%d", i-4); ret = dict_set_str (dict, key, (char *) words [i++]); if (ret < 0) goto out; } ret = dict_set_int32 (dict, "count", i - 4); if (ret < 0) goto out; goto set_type; } if (strcmp (w, "list-objects") == 0) { if (wordcount < 4) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_LIST_OBJECTS; i = 4; while (i < wordcount) { snprintf (key, 20, "path%d", i-4); ret = dict_set_str (dict, key, (char *) words[i++]); if (ret < 0) { gf_log ("cli", GF_LOG_ERROR, "Failed to set " "quota patch in request dictionary"); goto out; } } ret = dict_set_int32 (dict, "count", i - 4); if (ret < 0) { gf_log ("cli", GF_LOG_ERROR, "Failed to set quota " "limit count in request dictionary"); goto out; } goto set_type; } if (strcmp (w, "alert-time") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_ALERT_TIME; ret = gf_string2time (words[4], &time); if (ret) { cli_err ("Invalid argument %s. Please enter a valid " "string", words[4]); goto out; } ret = dict_set_str (dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp (w, "soft-timeout") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT; ret = gf_string2time (words[4], &time); if (ret) { cli_err ("Invalid argument %s. Please enter a valid " "string", words[4]); goto out; } ret = dict_set_str (dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp (w, "hard-timeout") == 0) { if(wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT; ret = gf_string2time (words[4], &time); if (ret) { cli_err ("Invalid argument %s. Please enter a valid " "string", words[4]); goto out; } ret = dict_set_str (dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp (w, "default-soft-limit") == 0) { if(wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT; ret = dict_set_str (dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } else { GF_ASSERT (!"opword mismatch"); } set_type: ret = dict_set_int32 (dict, "type", type); if (ret < 0) goto out; *options = dict; out: if (ret < 0) { if (dict) dict_destroy (dict); } return ret; } static inline gf_boolean_t cli_is_key_spl (char *key) { return (strcmp (key, "group") == 0); } static int cli_add_key_group (dict_t *dict, char *key, char *value, char **op_errstr) { int ret = -1; int opt_count = 0; char iter_key[1024] = {0,}; char iter_val[1024] = {0,}; char *saveptr = NULL; char *tok_key = NULL; char *tok_val = NULL; char *dkey = NULL; char *dval = NULL; char *tagpath = NULL; char *buf = NULL; char line[PATH_MAX + 256] = {0,}; char errstr[2048] = ""; FILE *fp = NULL; ret = gf_asprintf (&tagpath, "%s/groups/%s", GLUSTERD_DEFAULT_WORKDIR, value); if (ret == -1) { tagpath = NULL; goto out; } fp = fopen (tagpath, "r"); if (!fp) { ret = -1; snprintf(errstr, sizeof(errstr), "Unable to open file '%s'." " Error: %s", tagpath, strerror (errno)); if (op_errstr) *op_errstr = gf_strdup(errstr); goto out; } opt_count = 0; buf = line; while (fscanf (fp, "%s", buf) != EOF) { opt_count++; tok_key = strtok_r (line, "=", &saveptr); tok_val = strtok_r (NULL, "=", &saveptr); if (!tok_key || !tok_val) { ret = -1; snprintf(errstr, sizeof(errstr), "'%s' file format " "not valid.", tagpath); if (op_errstr) *op_errstr = gf_strdup(errstr); goto out; } snprintf (iter_key, sizeof (iter_key), "key%d", opt_count); dkey = gf_strdup (tok_key); ret = dict_set_dynstr (dict, iter_key, dkey); if (ret) goto out; dkey = NULL; snprintf (iter_val, sizeof (iter_val), "value%d", opt_count); dval = gf_strdup (tok_val); ret = dict_set_dynstr (dict, iter_val, dval); if (ret) goto out; dval = NULL; } if (!opt_count) { ret = -1; snprintf(errstr, sizeof(errstr), "'%s' file format " "not valid.", tagpath); if (op_errstr) *op_errstr = gf_strdup(errstr); goto out; } ret = dict_set_int32 (dict, "count", opt_count); out: GF_FREE (tagpath); if (ret) { GF_FREE (dkey); GF_FREE (dval); } if (fp) fclose (fp); return ret; } int32_t cli_cmd_volume_set_parse (struct cli_state *state, const char **words, int wordcount, dict_t **options, char **op_errstr) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; int count = 0; char *key = NULL; char *value = NULL; int i = 0; char str[50] = {0,}; const char *question = NULL; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (wordcount < 3) goto out; volname = (char *)words[2]; GF_ASSERT (volname); ret = dict_set_str (dict, "volname", volname); if (ret) goto out; if (!strcmp (volname, "all")) { ret = dict_set_str (dict, "globalname", "All"); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "dict set on global key failed."); goto out; } ret = dict_set_int32 (dict, "hold_global_locks", _gf_true); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "dict set on global key failed."); goto out; } } if ((!strcmp (volname, "help") || !strcmp (volname, "help-xml")) && wordcount == 3 ) { ret = dict_set_str (dict, volname, volname); if (ret) goto out; } else if (wordcount < 5) { ret = -1; goto out; } else if (wordcount == 5 && cli_is_key_spl ((char *)words[3])) { key = (char *) words[3]; value = (char *) words[4]; if ( !key || !value) { ret = -1; goto out; } ret = gf_strip_whitespace (value, strlen (value)); if (ret == -1) goto out; if (strlen (value) == 0) { ret = -1; goto out; } ret = cli_add_key_group (dict, key, value, op_errstr); if (ret == 0) *options = dict; goto out; } for (i = 3; i < wordcount; i+=2) { key = (char *) words[i]; value = (char *) words[i+1]; if ( !key || !value) { ret = -1; goto out; } count++; ret = gf_strip_whitespace (value, strlen (value)); if (ret == -1) goto out; if (strlen (value) == 0) { ret = -1; goto out; } if (cli_is_key_spl (key)) { ret = -1; goto out; } sprintf (str, "key%d", count); ret = dict_set_str (dict, str, key); if (ret) goto out; sprintf (str, "value%d", count); ret = dict_set_str (dict, str, value); if (ret) goto out; if ((!strcmp (key, "cluster.enable-shared-storage")) && (!strcmp (value, "disable"))) { question = "Disabling cluster.enable-shared-storage " "will delete the shared storage volume" "(gluster_shared_storage), which is used " "by snapshot scheduler, geo-replication " "and NFS-Ganesha. Do you still want to " "continue?"; answer = cli_cmd_get_confirmation (state, question); if (GF_ANSWER_NO == answer) { gf_log ("cli", GF_LOG_ERROR, "Operation " "cancelled, exiting"); *op_errstr = gf_strdup ("Aborted by user."); ret = -1; goto out; } } } ret = dict_set_int32 (dict, "count", wordcount-3); if (ret) goto out; *options = dict; out: if (ret) dict_destroy (dict); return ret; } int32_t cli_cmd_volume_add_brick_parse (const char **words, int wordcount, dict_t **options, int *ret_type) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; int brick_count = 0, brick_index = 0; char *bricks = NULL; char *opwords_cl[] = { "replica", "stripe", NULL }; gf1_cluster_type type = GF_CLUSTER_TYPE_NONE; int count = 1; char *w = NULL; int index; gf_boolean_t is_force = _gf_false; int wc = wordcount; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (wordcount < 3) goto out; volname = (char *)words[2]; GF_ASSERT (volname); ret = dict_set_str (dict, "volname", volname); if (ret) goto out; if (wordcount < 4) { ret = -1; goto out; } if (wordcount < 6) { /* seems no options are given, go directly to the parse_brick */ brick_index = 3; type = GF_CLUSTER_TYPE_NONE; goto parse_bricks; } w = str_getunamb (words[3], opwords_cl); if (!w) { type = GF_CLUSTER_TYPE_NONE; index = 3; } else if ((strcmp (w, "replica")) == 0) { type = GF_CLUSTER_TYPE_REPLICATE; count = strtol (words[4], NULL, 0); if (!count || (count < 2)) { cli_err ("replica count should be greater than 1"); ret = -1; goto out; } ret = dict_set_int32 (dict, "replica-count", count); if (ret) goto out; index = 5; } else if ((strcmp (w, "stripe")) == 0) { type = GF_CLUSTER_TYPE_STRIPE; count = strtol (words[4], NULL, 0); if (!count || (count < 2)) { cli_err ("stripe count should be greater than 1"); ret = -1; goto out; } ret = dict_set_int32 (dict, "stripe-count", count); if (ret) goto out; index = 5; } else { GF_ASSERT (!"opword mismatch"); ret = -1; goto out; } brick_index = index; parse_bricks: if (strcmp (words[wordcount - 1], "force") == 0) { is_force = _gf_true; wc = wordcount - 1; } ret = cli_cmd_bricks_parse (words, wc, brick_index, &bricks, &brick_count); if (ret) goto out; ret = dict_set_dynstr (dict, "bricks", bricks); if (ret) goto out; ret = dict_set_int32 (dict, "count", brick_count); if (ret) goto out; ret = dict_set_int32 (dict, "force", is_force); if (ret) goto out; *options = dict; out: if (ret_type) *ret_type = type; if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to parse add-brick CLI"); if (dict) dict_destroy (dict); } return ret; } int32_t cli_cmd_volume_detach_tier_parse (const char **words, int wordcount, dict_t **options) { int ret = -1; char *word = NULL; dict_t *dict = NULL; int32_t command = GF_OP_CMD_NONE; int force = 0; dict = dict_new (); if (!dict) goto out; ret = dict_set_str (dict, "volname", (char *)words[2]); if (ret) goto out; if (wordcount == 3 && !strcmp ((char *)words[2], "help")) { return -1; } if (!((wordcount == 4) || (wordcount == 5))) { ret = -1; goto out; } if (wordcount == 5) { word = (char *)words[4]; if (!strcmp(word, "force")) force = 1; else { ret = -1; goto out; } } word = (char *)words[3]; ret = -1; if (!strcmp(word, "start")) { command = GF_OP_CMD_DETACH_START; } else if (!strcmp(word, "commit")) { if (force) command = GF_OP_CMD_DETACH_COMMIT_FORCE; else command = GF_OP_CMD_DETACH_COMMIT; } else if (!strcmp(word, "stop")) command = GF_OP_CMD_STOP_DETACH_TIER; else if (!strcmp(word, "status")) command = GF_OP_CMD_STATUS; else goto out; ret = dict_set_int32 (dict, "command", command); if (ret) goto out; *options = dict; ret = 0; out: if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to parse detach-tier CLI"); if (dict) dict_unref (dict); } return ret; } int32_t cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, dict_t **options, int *question) { dict_t *dict = NULL; char *volname = NULL; char *delimiter = NULL; int ret = -1; char key[50]; int brick_count = 0, brick_index = 0; int32_t tmp_index = 0; int32_t j = 0; char *tmp_brick = NULL; char *tmp_brick1 = NULL; char *type_opword[] = { "replica", NULL }; char *opwords[] = { "start", "commit", "stop", "status", "force", NULL }; char *w = NULL; int32_t command = GF_OP_CMD_NONE; long count = 0; GF_ASSERT (words); GF_ASSERT (options); if (wordcount < 5) goto out; dict = dict_new (); if (!dict) goto out; volname = (char *)words[2]; GF_ASSERT (volname); ret = dict_set_str (dict, "volname", volname); if (ret) goto out; brick_index = 3; w = str_getunamb (words[3], type_opword); if (w && !strcmp ("replica", w)) { if (wordcount < 6) { ret = -1; goto out; } count = strtol (words[4], NULL, 0); if (count < 1) { cli_err ("replica count should be greater than 0 in " "case of remove-brick"); ret = -1; goto out; } ret = dict_set_int32 (dict, "replica-count", count); if (ret) goto out; brick_index = 5; } else if (w) { GF_ASSERT (!"opword mismatch"); } w = str_getunamb (words[wordcount - 1], opwords); if (!w) { ret = -1; goto out; } else { /* handled this option */ wordcount--; if (!strcmp ("start", w)) { command = GF_OP_CMD_START; } else if (!strcmp ("commit", w)) { command = GF_OP_CMD_COMMIT; if (question) *question = 1; } else if (!strcmp ("stop", w)) { command = GF_OP_CMD_STOP; } else if (!strcmp ("status", w)) { command = GF_OP_CMD_STATUS; } else if (!strcmp ("force", w)) { command = GF_OP_CMD_COMMIT_FORCE; if (question) *question = 1; } else { GF_ASSERT (!"opword mismatch"); ret = -1; goto out; } } ret = dict_set_int32 (dict, "command", command); if (ret) gf_log ("cli", GF_LOG_INFO, "failed to set 'command' %d", command); tmp_index = brick_index; tmp_brick = GF_MALLOC(2048 * sizeof(*tmp_brick), gf_common_mt_char); if (!tmp_brick) { gf_log ("",GF_LOG_ERROR,"cli_cmd_volume_remove_brick_parse: " "Unable to get memory"); ret = -1; goto out; } tmp_brick1 = GF_MALLOC(2048 * sizeof(*tmp_brick1), gf_common_mt_char); if (!tmp_brick1) { gf_log ("",GF_LOG_ERROR,"cli_cmd_volume_remove_brick_parse: " "Unable to get memory"); ret = -1; goto out; } while (brick_index < wordcount) { if (validate_brick_name ((char *)words[brick_index])) { cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[brick_index]); ret = -1; goto out; } else { delimiter = strrchr(words[brick_index], ':'); ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } j = tmp_index; strcpy(tmp_brick, words[brick_index]); while ( j < brick_index) { strcpy(tmp_brick1, words[j]); if (!(strcmp (tmp_brick, tmp_brick1))) { gf_log("",GF_LOG_ERROR, "Duplicate bricks" " found %s", words[brick_index]); cli_err("Duplicate bricks found %s", words[brick_index]); ret = -1; goto out; } j++; } snprintf (key, 50, "brick%d", ++brick_count); ret = dict_set_str (dict, key, (char *)words[brick_index++]); if (ret) goto out; } if (command != GF_OP_CMD_STATUS && command != GF_OP_CMD_STOP) { ret = dict_set_int32 (dict, "count", brick_count); if (ret) goto out; } *options = dict; out: if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to parse remove-brick CLI"); if (dict) dict_destroy (dict); } GF_FREE (tmp_brick); GF_FREE (tmp_brick1); return ret; } int32_t cli_cmd_volume_replace_brick_parse (const char **words, int wordcount, dict_t **options) { int ret = -1; char *volname = NULL; char *delimiter = NULL; dict_t *dict = NULL; GF_ASSERT (words); GF_ASSERT (options); if (wordcount != 7) { ret = -1; goto out; } dict = dict_new (); if (!dict) { gf_log ("cli", GF_LOG_ERROR, "Failed to allocate dictionary"); goto out; } volname = (char *)words[2]; GF_ASSERT (volname); ret = dict_set_str (dict, "volname", volname); if (ret) goto out; if (validate_brick_name ((char *)words[3])) { cli_err ("wrong brick type: %s, use " "<HOSTNAME>:<export-dir-abs-path>", words[3]); ret = -1; goto out; } else { delimiter = strrchr ((char *)words[3], ':'); ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } ret = dict_set_str (dict, "src-brick", (char *)words[3]); if (ret) goto out; if (validate_brick_name ((char *)words[4])) { cli_err ("wrong brick type: %s, use " "<HOSTNAME>:<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { delimiter = strrchr ((char *)words[4], ':'); ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } ret = dict_set_str (dict, "dst-brick", (char *)words[4]); if (ret) goto out; /* commit force option */ if (strcmp ("commit", words[5]) || strcmp ("force", words[6])) { cli_err ("Invalid option '%s' '%s' for replace-brick. Please " "enter valid replace-brick command", words[5], words[6]); ret = -1; goto out; } ret = dict_set_str (dict, "operation", "GF_REPLACE_OP_COMMIT_FORCE"); if (ret) goto out; *options = dict; out: if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to parse replace-brick CLI"); if (dict) dict_destroy (dict); } return ret; } int32_t cli_cmd_log_filename_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; char *str = NULL; int ret = -1; char *delimiter = NULL; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; volname = (char *)words[3]; GF_ASSERT (volname); ret = dict_set_str (dict, "volname", volname); if (ret) goto out; str = (char *)words[4]; if (strchr (str, ':')) { delimiter = strchr (words[4], ':'); if (!delimiter || delimiter == words[4] || *(delimiter+1) != '/') { cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } ret = dict_set_str (dict, "brick", str); if (ret) goto out; /* Path */ str = (char *)words[5]; ret = dict_set_str (dict, "path", str); if (ret) goto out; } else { ret = dict_set_str (dict, "path", str); if (ret) goto out; } *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } int32_t cli_cmd_log_level_parse (const char **words, int worcount, dict_t **options) { dict_t *dict = NULL; int ret = -1; GF_ASSERT (words); GF_ASSERT (options); /* * loglevel command format: * > volume log level <VOL> <XLATOR[*]> <LOGLEVEL> * > volume log level colon-o posix WARNING * > volume log level colon-o replicate* DEBUG * > volume log level coon-o * TRACE */ GF_ASSERT ((strncmp(words[0], "volume", 6) == 0)); GF_ASSERT ((strncmp(words[1], "log", 3) == 0)); GF_ASSERT ((strncmp(words[2], "level", 5) == 0)); ret = glusterd_check_log_level(words[5]); if (ret == -1) { cli_err("Invalid log level [%s] specified", words[5]); cli_err("Valid values for loglevel: (DEBUG|WARNING|ERROR" "|CRITICAL|NONE|TRACE)"); goto out; } dict = dict_new (); if (!dict) goto out; GF_ASSERT(words[3]); GF_ASSERT(words[4]); ret = dict_set_str (dict, "volname", (char *)words[3]); if (ret) goto out; ret = dict_set_str (dict, "xlator", (char *)words[4]); if (ret) goto out; ret = dict_set_str (dict, "loglevel", (char *)words[5]); if (ret) goto out; *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } int32_t cli_cmd_log_locate_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; char *str = NULL; int ret = -1; char *delimiter = NULL; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; volname = (char *)words[3]; GF_ASSERT (volname); ret = dict_set_str (dict, "volname", volname); if (ret) goto out; if (words[4]) { delimiter = strchr (words[4], ':'); if (!delimiter || delimiter == words[4] || *(delimiter+1) != '/') { cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } str = (char *)words[4]; ret = dict_set_str (dict, "brick", str); if (ret) goto out; } *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } int32_t cli_cmd_log_rotate_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; char *str = NULL; int ret = -1; char *delimiter = NULL; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (strcmp ("rotate", words[3]) == 0) volname = (char *)words[2]; else if (strcmp ("rotate", words[2]) == 0) volname = (char *)words[3]; GF_ASSERT (volname); ret = dict_set_str (dict, "volname", volname); if (ret) goto out; if (words[4]) { delimiter = strchr (words[4], ':'); if (!delimiter || delimiter == words[4] || *(delimiter+1) != '/') { cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } str = (char *)words[4]; ret = dict_set_str (dict, "brick", str); if (ret) goto out; } *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } static gf_boolean_t gsyncd_url_check (const char *w) { return !!strpbrk (w, ":/"); } static gf_boolean_t gsyncd_glob_check (const char *w) { return !!strpbrk (w, "*?["); } static int config_parse (const char **words, int wordcount, dict_t *dict, unsigned cmdi, unsigned glob) { int32_t ret = -1; int32_t i = -1; char *append_str = NULL; size_t append_len = 0; char *subop = NULL; switch ((wordcount - 1) - cmdi) { case 0: subop = gf_strdup ("get-all"); break; case 1: if (words[cmdi + 1][0] == '!') { (words[cmdi + 1])++; if (gf_asprintf (&subop, "del%s", glob ? "-glob" : "") == -1) subop = NULL; } else subop = gf_strdup ("get"); ret = dict_set_str (dict, "op_name", ((char *)words[cmdi + 1])); if (ret < 0) goto out; break; default: if (gf_asprintf (&subop, "set%s", glob ? "-glob" : "") == -1) subop = NULL; ret = dict_set_str (dict, "op_name", ((char *)words[cmdi + 1])); if (ret < 0) goto out; /* join the varargs by spaces to get the op_value */ for (i = cmdi + 2; i < wordcount; i++) append_len += (strlen (words[i]) + 1); /* trailing strcat will add two bytes, make space for that */ append_len++; append_str = GF_CALLOC (1, append_len, cli_mt_append_str); if (!append_str) { ret = -1; goto out; } for (i = cmdi + 2; i < wordcount; i++) { strcat (append_str, words[i]); strcat (append_str, " "); } append_str[append_len - 2] = '\0'; /* "checkpoint now" is special: we resolve that "now" */ if ((strcmp (words[cmdi + 1], "checkpoint") == 0) && (strcmp (append_str, "now") == 0)) { struct timeval tv = {0,}; ret = gettimeofday (&tv, NULL); if (ret == -1) goto out; GF_FREE (append_str); append_str = GF_CALLOC (1, 300, cli_mt_append_str); if (!append_str) { ret = -1; goto out; } snprintf (append_str, 300, "%" GF_PRI_SECOND, tv.tv_sec); } ret = dict_set_dynstr (dict, "op_value", append_str); } ret = -1; if (subop) { ret = dict_set_dynstr (dict, "subop", subop); if (!ret) subop = NULL; } out: if (ret && append_str) GF_FREE (append_str); GF_FREE (subop); gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; } static int32_t force_push_pem_no_verify_parse (const char **words, int wordcount, dict_t *dict, unsigned *cmdi) { int32_t ret = 0; if (!strcmp ((char *)words[wordcount-1], "force")) { if ((strcmp ((char *)words[wordcount-2], "start")) && (strcmp ((char *)words[wordcount-2], "stop")) && (strcmp ((char *)words[wordcount-2], "create")) && (strcmp ((char *)words[wordcount-2], "no-verify")) && (strcmp ((char *)words[wordcount-2], "push-pem")) && (strcmp ((char *)words[wordcount-2], "pause")) && (strcmp ((char *)words[wordcount-2], "resume"))) { ret = -1; goto out; } ret = dict_set_uint32 (dict, "force", _gf_true); if (ret) goto out; (*cmdi)++; if (!strcmp ((char *)words[wordcount-2], "push-pem")) { if (strcmp ((char *)words[wordcount-3], "create")) { ret = -1; goto out; } ret = dict_set_int32 (dict, "push_pem", 1); if (ret) goto out; (*cmdi)++; } else if (!strcmp ((char *)words[wordcount-2], "no-verify")) { if (strcmp ((char *)words[wordcount-3], "create")) { ret = -1; goto out; } ret = dict_set_uint32 (dict, "no_verify", _gf_true); if (ret) goto out; (*cmdi)++; } } else if (!strcmp ((char *)words[wordcount-1], "push-pem")) { if (strcmp ((char *)words[wordcount-2], "create")) { ret = -1; goto out; } ret = dict_set_int32 (dict, "push_pem", 1); if (ret) goto out; (*cmdi)++; } else if (!strcmp ((char *)words[wordcount-1], "no-verify")) { if ((strcmp ((char *)words[wordcount-2], "create"))) { ret = -1; goto out; } ret = dict_set_uint32 (dict, "no_verify", _gf_true); if (ret) goto out; (*cmdi)++; } out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; } int32_t cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) { int32_t ret = -1; dict_t *dict = NULL; gf1_cli_gsync_set type = GF_GSYNC_OPTION_TYPE_NONE; int i = 0; unsigned masteri = 0; unsigned slavei = 0; unsigned glob = 0; unsigned cmdi = 0; char *opwords[] = { "create", "status", "start", "stop", "config", "force", "delete", "no-verify" "push-pem", "detail", "pause", "resume", NULL }; char *w = NULL; char *save_ptr = NULL; char *slave_temp = NULL; char *token = NULL; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; /* new syntax: * * volume geo-replication $m $s create [[no-verify] | [push-pem]] [force] * volume geo-replication [$m [$s]] status [detail] * volume geo-replication [$m] $s config [[!]$opt [$val]] * volume geo-replication $m $s start|stop [force] * volume geo-replication $m $s delete * volume geo-replication $m $s pause [force] * volume geo-replication $m $s resume [force] */ if (wordcount < 3) goto out; for (i = 2; i <= 3 && i < wordcount - 1; i++) { if (gsyncd_glob_check (words[i])) glob = i; if (gsyncd_url_check (words[i])) { slavei = i; break; } } if (glob && !slavei) /* glob is allowed only for config, thus it implies there is a * slave argument; but that might have not been recognized on * the first scan as it's url characteristics has been covered * by the glob syntax. * * In this case, the slave is perforce the last glob-word -- the * upcoming one is neither glob, nor url, so it's definitely not * the slave. */ slavei = glob; if (slavei) { cmdi = slavei + 1; if (slavei == 3) masteri = 2; } else if (i <= 3) { if (!strcmp ((char *)words[wordcount-1], "detail")) { /* For status detail it is mandatory to provide * both master and slave */ ret = -1; goto out; } /* no $s, can only be status cmd * (with either a single $m before it or nothing) * -- these conditions imply that i <= 3 after * the iteration and that i is the successor of * the (0 or 1 length) sequence of $m-s. */ cmdi = i; if (i == 3) masteri = 2; } else goto out; /* now check if input really complies syntax * (in a somewhat redundant way, in favor * transparent soundness) */ if (masteri && gsyncd_url_check (words[masteri])) goto out; if (slavei && !glob && !gsyncd_url_check (words[slavei])) goto out; w = str_getunamb (words[cmdi], opwords); if (!w) goto out; if (strcmp (w, "create") == 0) { type = GF_GSYNC_OPTION_TYPE_CREATE; if (!masteri || !slavei) goto out; } else if (strcmp (w, "status") == 0) { type = GF_GSYNC_OPTION_TYPE_STATUS; if (slavei && !masteri) goto out; } else if (strcmp (w, "config") == 0) { type = GF_GSYNC_OPTION_TYPE_CONFIG; if (!slavei) goto out; } else if (strcmp (w, "start") == 0) { type = GF_GSYNC_OPTION_TYPE_START; if (!masteri || !slavei) goto out; } else if (strcmp (w, "stop") == 0) { type = GF_GSYNC_OPTION_TYPE_STOP; if (!masteri || !slavei) goto out; } else if (strcmp (w, "delete") == 0) { type = GF_GSYNC_OPTION_TYPE_DELETE; if (!masteri || !slavei) goto out; } else if (strcmp (w, "pause") == 0) { type = GF_GSYNC_OPTION_TYPE_PAUSE; if (!masteri || !slavei) goto out; } else if (strcmp (w, "resume") == 0) { type = GF_GSYNC_OPTION_TYPE_RESUME; if (!masteri || !slavei) goto out; } else GF_ASSERT (!"opword mismatch"); ret = force_push_pem_no_verify_parse (words, wordcount, dict, &cmdi); if (ret) goto out; if (!strcmp ((char *)words[wordcount-1], "detail")) { if (strcmp ((char *)words[wordcount-2], "status")) { ret = -1; goto out; } if (!slavei || !masteri) { ret = -1; goto out; } ret = dict_set_uint32 (dict, "status-detail", _gf_true); if (ret) goto out; cmdi++; } if (type != GF_GSYNC_OPTION_TYPE_CONFIG && (cmdi < wordcount - 1 || glob)) goto out; /* If got so far, input is valid, assemble the message */ ret = 0; if (masteri) { ret = dict_set_str (dict, "master", (char *)words[masteri]); if (!ret) ret = dict_set_str (dict, "volname", (char *)words[masteri]); } if (!ret && slavei) { /* If geo-rep is created with root user using the syntax * gluster vol geo-rep <mastervol> root@<slavehost> ... * pass down only <slavehost> else pass as it is. */ slave_temp = gf_strdup (words[slavei]); token = strtok_r (slave_temp, "@", &save_ptr); if (token && !strcmp (token, "root")) { ret = dict_set_str (dict, "slave", (char *)words[slavei]+5); } else { ret = dict_set_str (dict, "slave", (char *)words[slavei]); } } if (!ret) ret = dict_set_int32 (dict, "type", type); if (!ret && type == GF_GSYNC_OPTION_TYPE_CONFIG) ret = config_parse (words, wordcount, dict, cmdi, glob); out: if (slave_temp) GF_FREE (slave_temp); if (ret) { if (dict) dict_destroy (dict); } else *options = dict; return ret; } int32_t cli_cmd_volume_profile_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; gf1_cli_stats_op op = GF_CLI_STATS_NONE; gf1_cli_info_op info_op = GF_CLI_INFO_NONE; gf_boolean_t is_peek = _gf_false; char *opwords[] = { "start", "stop", "info", NULL }; char *w = NULL; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (wordcount < 4) goto out; volname = (char *)words[2]; ret = dict_set_str (dict, "volname", volname); if (ret) goto out; w = str_getunamb (words[3], opwords); if (!w) { ret = -1; goto out; } if ((strcmp (w, "start") == 0 || strcmp (w, "stop") == 0) && wordcount > 5) goto out; if (strcmp (w, "info") == 0 && wordcount > 7) goto out; if (strcmp (w, "start") == 0) { op = GF_CLI_STATS_START; } else if (strcmp (w, "stop") == 0) { op = GF_CLI_STATS_STOP; } else if (strcmp (w, "info") == 0) { op = GF_CLI_STATS_INFO; info_op = GF_CLI_INFO_ALL; if (wordcount > 4) { if (strcmp (words[4], "incremental") == 0) { info_op = GF_CLI_INFO_INCREMENTAL; if (wordcount > 5 && strcmp (words[5], "peek") == 0) { is_peek = _gf_true; } } else if (strcmp (words[4], "cumulative") == 0) { info_op = GF_CLI_INFO_CUMULATIVE; } else if (strcmp (words[4], "clear") == 0) { info_op = GF_CLI_INFO_CLEAR; } else if (strcmp (words[4], "peek") == 0) { is_peek = _gf_true; } } } else GF_ASSERT (!"opword mismatch"); ret = dict_set_int32 (dict, "op", (int32_t)op); if (ret) goto out; ret = dict_set_int32 (dict, "info-op", (int32_t)info_op); if (ret) goto out; ret = dict_set_int32 (dict, "peek", is_peek); if (ret) goto out; if (!strcmp (words[wordcount - 1], "nfs")) { ret = dict_set_int32 (dict, "nfs", _gf_true); if (ret) goto out; } *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } int32_t cli_cmd_volume_top_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; char *value = NULL; char *key = NULL; int ret = -1; gf1_cli_stats_op op = GF_CLI_STATS_NONE; gf1_cli_top_op top_op = GF_CLI_TOP_NONE; int32_t list_cnt = -1; int index = 0; int perf = 0; int32_t blk_size = 0; uint32_t count = 0; gf_boolean_t nfs = _gf_false; char *delimiter = NULL; char *opwords[] = { "open", "read", "write", "opendir", "readdir", "read-perf", "write-perf", "clear", NULL }; char *w = NULL; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (wordcount < 4) goto out; volname = (char *)words[2]; ret = dict_set_str (dict, "volname", volname); if (ret) goto out; op = GF_CLI_STATS_TOP; ret = dict_set_int32 (dict, "op", (int32_t)op); if (ret) goto out; w = str_getunamb (words[3], opwords); if (!w) { ret = -1; goto out; } if (strcmp (w, "open") == 0) { top_op = GF_CLI_TOP_OPEN; } else if (strcmp (w, "read") == 0) { top_op = GF_CLI_TOP_READ; } else if (strcmp (w, "write") == 0) { top_op = GF_CLI_TOP_WRITE; } else if (strcmp (w, "opendir") == 0) { top_op = GF_CLI_TOP_OPENDIR; } else if (strcmp (w, "readdir") == 0) { top_op = GF_CLI_TOP_READDIR; } else if (strcmp (w, "read-perf") == 0) { top_op = GF_CLI_TOP_READ_PERF; perf = 1; } else if (strcmp (w, "write-perf") == 0) { top_op = GF_CLI_TOP_WRITE_PERF; perf = 1; } else if (strcmp (w, "clear") == 0) { ret = dict_set_int32 (dict, "clear-stats", 1); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not set clear-stats in dict"); goto out; } } else GF_ASSERT (!"opword mismatch"); ret = dict_set_int32 (dict, "top-op", (int32_t)top_op); if (ret) goto out; if ((wordcount > 4) && !strcmp (words[4], "nfs")) { nfs = _gf_true; ret = dict_set_int32 (dict, "nfs", nfs); if (ret) goto out; index = 5; } else { index = 4; } for (; index < wordcount; index+=2) { key = (char *) words[index]; value = (char *) words[index+1]; if ( key && !value ) { ret = -1; goto out; } if (!strcmp (key, "brick")) { delimiter = strchr (value, ':'); if (!delimiter || delimiter == value || *(delimiter+1) != '/') { cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", value); ret = -1; goto out; } else { ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } ret = dict_set_str (dict, "brick", value); } else if (!strcmp (key, "list-cnt")) { ret = gf_is_str_int (value); if (!ret) list_cnt = atoi (value); if (ret || (list_cnt < 0) || (list_cnt > 100)) { cli_err ("list-cnt should be between 0 to 100"); ret = -1; goto out; } } else if (perf && !nfs && !strcmp (key, "bs")) { ret = gf_is_str_int (value); if (!ret) blk_size = atoi (value); if (ret || (blk_size <= 0)) { if (blk_size < 0) cli_err ("block size is an invalid" " number"); else cli_err ("block size should be an " "integer greater than zero"); ret = -1; goto out; } ret = dict_set_uint32 (dict, "blk-size", (uint32_t)blk_size); } else if (perf && !nfs && !strcmp (key, "count")) { ret = gf_is_str_int (value); if (!ret) count = atoi(value); if (ret || (count <= 0)) { if (count < 0) cli_err ("count is an invalid number"); else cli_err ("count should be an integer " "greater than zero"); ret = -1; goto out; } ret = dict_set_uint32 (dict, "blk-cnt", count); } else { ret = -1; goto out; } if (ret) { gf_log ("", GF_LOG_WARNING, "Dict set failed for " "key %s", key); goto out; } } if (list_cnt == -1) list_cnt = 100; ret = dict_set_int32 (dict, "list-cnt", list_cnt); if (ret) { gf_log ("", GF_LOG_WARNING, "Dict set failed for list_cnt"); goto out; } if ((blk_size > 0) ^ (count > 0)) { cli_err ("Need to give both 'bs' and 'count'"); ret = -1; goto out; } else if (((uint64_t)blk_size * count) > (10 * GF_UNIT_GB)) { cli_err ("'bs * count' value %"PRIu64" is greater than " "maximum allowed value of 10GB", ((uint64_t)blk_size * count)); ret = -1; goto out; } *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } uint32_t cli_cmd_get_statusop (const char *arg) { int i = 0; uint32_t ret = GF_CLI_STATUS_NONE; char *w = NULL; char *opwords[] = {"detail", "mem", "clients", "fd", "inode", "callpool", "tasks", NULL}; struct { char *opname; uint32_t opcode; } optable[] = { { "detail", GF_CLI_STATUS_DETAIL }, { "mem", GF_CLI_STATUS_MEM }, { "clients", GF_CLI_STATUS_CLIENTS }, { "fd", GF_CLI_STATUS_FD }, { "inode", GF_CLI_STATUS_INODE }, { "callpool", GF_CLI_STATUS_CALLPOOL }, { "tasks", GF_CLI_STATUS_TASKS }, { NULL } }; w = str_getunamb (arg, opwords); if (!w) { gf_log ("cli", GF_LOG_DEBUG, "Not a status op %s", arg); goto out; } for (i = 0; optable[i].opname; i++) { if (!strcmp (w, optable[i].opname)) { ret = optable[i].opcode; break; } } out: return ret; } int cli_cmd_volume_status_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; int ret = -1; uint32_t cmd = 0; GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; switch (wordcount) { case 2: cmd = GF_CLI_STATUS_ALL; ret = 0; break; case 3: if (!strcmp (words[2], "all")) { cmd = GF_CLI_STATUS_ALL; ret = 0; } else { cmd = GF_CLI_STATUS_VOL; ret = dict_set_str (dict, "volname", (char *)words[2]); } break; case 4: cmd = cli_cmd_get_statusop (words[3]); if (!strcmp (words[2], "all")) { if (cmd == GF_CLI_STATUS_NONE) { cli_err ("%s is not a valid status option", words[3]); ret = -1; goto out; } cmd |= GF_CLI_STATUS_ALL; ret = 0; } else { ret = dict_set_str (dict, "volname", (char *)words[2]); if (ret) goto out; if (cmd == GF_CLI_STATUS_NONE) { if (!strcmp (words[3], "nfs")) { cmd |= GF_CLI_STATUS_NFS; } else if (!strcmp (words[3], "shd")) { cmd |= GF_CLI_STATUS_SHD; } else if (!strcmp (words[3], "quotad")) { cmd |= GF_CLI_STATUS_QUOTAD; } else if (!strcmp (words[3], "snapd")) { cmd |= GF_CLI_STATUS_SNAPD; } else if (!strcmp (words[3], "bitd")) { cmd |= GF_CLI_STATUS_BITD; } else if (!strcmp (words[3], "scrub")) { cmd |= GF_CLI_STATUS_SCRUB; } else { cmd = GF_CLI_STATUS_BRICK; ret = dict_set_str (dict, "brick", (char *)words[3]); } } else { cmd |= GF_CLI_STATUS_VOL; ret = 0; } } break; case 5: if (!strcmp (words[2], "all")) { cli_err ("Cannot specify brick/nfs for \"all\""); ret = -1; goto out; } cmd = cli_cmd_get_statusop (words[4]); if (cmd == GF_CLI_STATUS_NONE) { cli_err ("%s is not a valid status option", words[4]); ret = -1; goto out; } ret = dict_set_str (dict, "volname", (char *)words[2]); if (ret) goto out; if (!strcmp (words[3], "nfs")) { if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_TASKS) { cli_err ("Detail/FD/Tasks status not available" " for NFS Servers"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_NFS; } else if (!strcmp (words[3], "shd")){ if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_TASKS) { cli_err ("Detail/FD/Clients/Tasks status not " "available for Self-heal Daemons"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_SHD; } else if (!strcmp (words[3], "quotad")) { if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) { cli_err ("Detail/FD/Clients/Inode status not " "available for Quota Daemon"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_QUOTAD; } else if (!strcmp (words[3], "snapd")) { if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) { cli_err ("Detail/FD/Clients/Inode status not " "available for snap daemon"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_SNAPD; } else { if (cmd == GF_CLI_STATUS_TASKS) { cli_err ("Tasks status not available for " "bricks"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_BRICK; ret = dict_set_str (dict, "brick", (char *)words[3]); } break; default: goto out; } if (ret) goto out; ret = dict_set_int32 (dict, "cmd", cmd); if (ret) goto out; *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } gf_boolean_t cli_cmd_validate_dumpoption (const char *arg, char **option) { char *opwords[] = {"all", "nfs", "mem", "iobuf", "callpool", "priv", "fd", "inode", "history", "inodectx", "fdctx", "quotad", NULL}; char *w = NULL; w = str_getunamb (arg, opwords); if (!w) { gf_log ("cli", GF_LOG_DEBUG, "Unknown statedump option %s", arg); return _gf_false; } *option = w; return _gf_true; } int cli_cmd_volume_statedump_options_parse (const char **words, int wordcount, dict_t **options) { int ret = 0; int i = 0; dict_t *dict = NULL; int option_cnt = 0; char *option = NULL; char option_str[100] = {0,}; for (i = 3; i < wordcount; i++, option_cnt++) { if (!cli_cmd_validate_dumpoption (words[i], &option)) { ret = -1; goto out; } strncat (option_str, option, strlen (option)); strncat (option_str, " ", 1); } if((strstr (option_str, "nfs")) && strstr (option_str, "quotad")) { ret = -1; goto out; } dict = dict_new (); if (!dict) goto out; ret = dict_set_dynstr (dict, "options", gf_strdup (option_str)); if (ret) goto out; ret = dict_set_int32 (dict, "option_cnt", option_cnt); if (ret) goto out; *options = dict; out: if (ret && dict) dict_destroy (dict); if (ret) gf_log ("cli", GF_LOG_ERROR, "Error parsing dumpoptions"); return ret; } int cli_cmd_volume_clrlks_opts_parse (const char **words, int wordcount, dict_t **options) { int ret = -1; int i = 0; dict_t *dict = NULL; char *kind_opts[4] = {"blocked", "granted", "all", NULL}; char *types[4] = {"inode", "entry", "posix", NULL}; char *free_ptr = NULL; dict = dict_new (); if (!dict) goto out; if (strcmp (words[4], "kind")) goto out; for (i = 0; kind_opts[i]; i++) { if (!strcmp (words[5], kind_opts[i])) { free_ptr = gf_strdup (words[5]); ret = dict_set_dynstr (dict, "kind", free_ptr); if (ret) goto out; free_ptr = NULL; break; } } if (i == 3) goto out; ret = -1; for (i = 0; types[i]; i++) { if (!strcmp (words[6], types[i])) { free_ptr = gf_strdup (words[6]); ret = dict_set_dynstr (dict, "type", free_ptr); if (ret) goto out; free_ptr = NULL; break; } } if (i == 3) goto out; if (wordcount == 8) { free_ptr = gf_strdup (words[7]); ret = dict_set_dynstr (dict, "opts", free_ptr); if (ret) goto out; free_ptr = NULL; } ret = 0; *options = dict; out: if (ret) { GF_FREE (free_ptr); dict_unref (dict); } return ret; } static int extract_hostname_path_from_token (const char *tmp_words, char **hostname, char **path) { int ret = 0; char *delimiter = NULL; char *tmp_host = NULL; char *host_name = NULL; char *words = NULL; *hostname = NULL; *path = NULL; words = GF_CALLOC (1, strlen (tmp_words) + 1, gf_common_mt_char); if (!words){ ret = -1; goto out; } strncpy (words, tmp_words, strlen (tmp_words) + 1); if (validate_brick_name (words)) { cli_err ("Wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words); ret = -1; goto out; } else { delimiter = strrchr (words, ':'); ret = gf_canonicalize_path (delimiter + 1); if (ret) { goto out; } else { *path = GF_CALLOC (1, strlen (delimiter+1) +1, gf_common_mt_char); if (!*path) { ret = -1; goto out; } strncpy (*path, delimiter +1, strlen(delimiter + 1) + 1); } } tmp_host = gf_strdup (words); if (!tmp_host) { gf_log ("cli", GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } get_host_name (tmp_host, &host_name); if (!host_name) { ret = -1; gf_log("cli",GF_LOG_ERROR, "Unable to allocate " "memory"); goto out; } if (!(strcmp (host_name, "localhost") && strcmp (host_name, "127.0.0.1") && strncmp (host_name, "0.", 2))) { cli_err ("Please provide a valid hostname/ip other " "than localhost, 127.0.0.1 or loopback " "address (0.0.0.0 to 0.255.255.255)."); ret = -1; goto out; } if (!valid_internet_address (host_name, _gf_false)) { cli_err ("internet address '%s' does not conform to " "standards", host_name); ret = -1; goto out; } *hostname = GF_CALLOC (1, strlen (host_name) + 1, gf_common_mt_char); if (!*hostname) { ret = -1; goto out; } strncpy (*hostname, host_name, strlen (host_name) + 1); ret = 0; out: GF_FREE (words); GF_FREE (tmp_host); return ret; } static int set_hostname_path_in_dict (const char *token, dict_t *dict, int heal_op) { char *hostname = NULL; char *path = NULL; int ret = 0; ret = extract_hostname_path_from_token (token, &hostname, &path); if (ret) goto out; switch (heal_op) { case GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK: ret = dict_set_dynstr (dict, "heal-source-hostname", hostname); if (ret) goto out; ret = dict_set_dynstr (dict, "heal-source-brickpath", path); break; case GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA: ret = dict_set_dynstr (dict, "per-replica-cmd-hostname", hostname); if (ret) goto out; ret = dict_set_dynstr (dict, "per-replica-cmd-path", path); break; default: ret = -1; break; } out: return ret; } static int heal_command_type_get (const char *command) { int i = 0; /* subcommands are set as NULL */ char *heal_cmds[GF_SHD_OP_HEAL_DISABLE + 1] = { [GF_SHD_OP_INVALID] = NULL, [GF_SHD_OP_HEAL_INDEX] = NULL, [GF_SHD_OP_HEAL_FULL] = "full", [GF_SHD_OP_INDEX_SUMMARY] = "info", [GF_SHD_OP_HEALED_FILES] = NULL, [GF_SHD_OP_HEAL_FAILED_FILES] = NULL, [GF_SHD_OP_SPLIT_BRAIN_FILES] = NULL, [GF_SHD_OP_STATISTICS] = "statistics", [GF_SHD_OP_STATISTICS_HEAL_COUNT] = NULL, [GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA] = NULL, [GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE] = NULL, [GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK] = NULL, [GF_SHD_OP_HEAL_ENABLE] = "enable", [GF_SHD_OP_HEAL_DISABLE] = "disable", }; for (i = 0; i <= GF_SHD_OP_HEAL_DISABLE; i++) { if (heal_cmds[i] && (strcmp (heal_cmds[i], command) == 0)) return i; } return GF_SHD_OP_INVALID; } int cli_cmd_volume_heal_options_parse (const char **words, int wordcount, dict_t **options) { int ret = 0; dict_t *dict = NULL; char *hostname = NULL; char *path = NULL; gf_xl_afr_op_t op = GF_SHD_OP_INVALID; dict = dict_new (); if (!dict) goto out; ret = dict_set_str (dict, "volname", (char *) words[2]); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "failed to set volname"); goto out; } if (wordcount == 3) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_HEAL_INDEX); goto done; } if (wordcount == 4) { op = heal_command_type_get (words[3]); if (op == GF_SHD_OP_INVALID) { ret = -1; goto out; } ret = dict_set_int32 (dict, "heal-op", op); goto done; } if (wordcount == 5) { if (strcmp (words[3], "info") && strcmp (words[3], "statistics")) { ret = -1; goto out; } if (!strcmp (words[3], "info")) { if (!strcmp (words[4], "healed")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_HEALED_FILES); goto done; } if (!strcmp (words[4], "heal-failed")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_HEAL_FAILED_FILES); goto done; } if (!strcmp (words[4], "split-brain")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_SPLIT_BRAIN_FILES); goto done; } } if (!strcmp (words[3], "statistics")) { if (!strcmp (words[4], "heal-count")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_STATISTICS_HEAL_COUNT); goto done; } } ret = -1; goto out; } if (wordcount == 6) { if (strcmp (words[3], "split-brain")) { ret = -1; goto out; } if (!strcmp (words[4], "bigger-file")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE); if (ret) goto out; ret = dict_set_str (dict, "file", (char *)words[5]); if (ret) goto out; goto done; } if (!strcmp (words[4], "source-brick")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); if (ret) goto out; ret = set_hostname_path_in_dict (words[5], dict, GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); if (ret) goto out; goto done; } ret = -1; goto out; } if (wordcount == 7) { if (!strcmp (words[3], "statistics") && !strcmp (words[4], "heal-count") && !strcmp (words[5], "replica")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA); if (ret) goto out; ret = set_hostname_path_in_dict (words[6], dict, GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA); if (ret) goto out; goto done; } if (!strcmp (words[3], "split-brain") && !strcmp (words[4], "source-brick")) { ret = dict_set_int32 (dict, "heal-op", GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); ret = set_hostname_path_in_dict (words[5], dict, GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); if (ret) goto out; ret = dict_set_str (dict, "file", (char *) words[6]); if (ret) goto out; goto done; } } ret = -1; goto out; done: *options = dict; out: if (ret && dict) { dict_unref (dict); *options = NULL; } return ret; } int cli_cmd_volume_defrag_parse (const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; int ret = -1; char *option = NULL; char *volname = NULL; char *command = NULL; gf_cli_defrag_type cmd = 0; GF_ASSERT (words); GF_ASSERT (options); dict = dict_new (); if (!dict) goto out; if (!((wordcount == 4) || (wordcount == 5))) goto out; if (wordcount == 4) { if (strcmp (words[3], "start") && strcmp (words[3], "stop") && strcmp (words[3], "status")) goto out; } else if ((strcmp (words[3], "tier") == 0) && (strcmp (words[4], "start") == 0)) { volname = (char *) words[2]; cmd = GF_DEFRAG_CMD_START_TIER; goto done; } else if ((strcmp (words[3], "tier") == 0) && (strcmp (words[4], "status") == 0)) { volname = (char *) words[2]; cmd = GF_DEFRAG_CMD_STATUS_TIER; goto done; } else { if (strcmp (words[3], "fix-layout") && strcmp (words[3], "start")) goto out; } volname = (char *) words[2]; if (wordcount == 4) { command = (char *) words[3]; } if (wordcount == 5) { if ((strcmp (words[3], "fix-layout") || strcmp (words[4], "start")) && (strcmp (words[3], "start") || strcmp (words[4], "force"))) { ret = -1; goto out; } command = (char *) words[3]; option = (char *) words[4]; } if (strcmp (command, "start") == 0) { cmd = GF_DEFRAG_CMD_START; if (option && strcmp (option, "force") == 0) { cmd = GF_DEFRAG_CMD_START_FORCE; } goto done; } if (strcmp (command, "fix-layout") == 0) { cmd = GF_DEFRAG_CMD_START_LAYOUT_FIX; goto done; } if (strcmp (command, "stop") == 0) { cmd = GF_DEFRAG_CMD_STOP; goto done; } if (strcmp (command, "status") == 0) { cmd = GF_DEFRAG_CMD_STATUS; } done: ret = dict_set_str (dict, "volname", volname); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "failed to set dict"); goto out; } ret = dict_set_int32 (dict, "rebalance-command", (int32_t) cmd); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "failed to set dict"); goto out; } *options = dict; out: if (ret && dict) dict_destroy (dict); return ret; } int32_t cli_snap_create_desc_parse (dict_t *dict, const char **words, size_t wordcount, int32_t desc_opt_loc) { int32_t ret = -1; char *desc = NULL; int32_t desc_len = 0; desc = GF_CALLOC (MAX_SNAP_DESCRIPTION_LEN + 1, sizeof(char), gf_common_mt_char); if (!desc) { ret = -1; goto out; } if (strlen (words[desc_opt_loc]) >= MAX_SNAP_DESCRIPTION_LEN) { cli_out ("snapshot create: description truncated: " "Description provided is longer than 1024 characters"); desc_len = MAX_SNAP_DESCRIPTION_LEN; } else { desc_len = strlen (words[desc_opt_loc]); } strncpy (desc, words[desc_opt_loc], desc_len); desc[desc_len] = '\0'; /* Calculating the size of the description as given by the user */ ret = dict_set_dynstr (dict, "description", desc); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to save snap " "description"); goto out; } ret = 0; out: if (ret && desc) GF_FREE (desc); return ret; } /* Function to check whether the Volume name is repeated */ int cli_check_if_volname_repeated (const char **words, unsigned int start_index, uint64_t cur_index) { uint64_t i = -1; int ret = 0; GF_ASSERT (words); for (i = start_index ; i < cur_index ; i++) { if (strcmp (words[i], words[cur_index]) == 0) { ret = -1; goto out; } } out: return ret; } /* snapshot clone <clonename> <snapname> * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_clone_parse (dict_t *dict, const char **words, int wordcount) { uint64_t i = 0; int ret = -1; char key[PATH_MAX] = ""; char *clonename = NULL; unsigned int cmdi = 2; int flags = 0; /* cmdi is command index, here cmdi is "2" (gluster snapshot clone)*/ GF_ASSERT (words); GF_ASSERT (dict); if (wordcount == cmdi + 1) { cli_err ("Invalid Syntax."); gf_log ("cli", GF_LOG_ERROR, "Invalid number of words for snap clone command"); goto out; } if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { cli_err ("snapshot clone: failed: clonename cannot exceed " "255 characters."); gf_log ("cli", GF_LOG_ERROR, "Clone name too long"); goto out; } clonename = (char *) words[cmdi]; for (i = 0 ; i < strlen (clonename); i++) { /* Following volume name convention */ if (!isalnum (clonename[i]) && (clonename[i] != '_' && (clonename[i] != '-'))) { /* TODO : Is this message enough?? */ cli_err ("Clonename can contain only alphanumeric, " "\"-\" and \"_\" characters"); goto out; } } ret = dict_set_int32 (dict, "volcount", 1); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save volcount"); goto out; } ret = dict_set_str (dict, "clonename", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save clone " "name(%s)", (char *)words[cmdi]); goto out; } /* Filling snap name in the dictionary */ ret = dict_set_str (dict, "snapname", (char *)words[cmdi+1]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not " "save snap name(%s)", (char *)words[cmdi+1]); goto out; } ret = 0; out: return ret; } /* snapshot create <snapname> <vol-name(s)> [description <description>] * [force] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_create_parse (dict_t *dict, const char **words, int wordcount) { uint64_t i = 0; int ret = -1; uint64_t volcount = 0; char key[PATH_MAX] = ""; char *snapname = NULL; unsigned int cmdi = 2; int flags = 0; /* cmdi is command index, here cmdi is "2" (gluster snapshot create)*/ GF_ASSERT (words); GF_ASSERT (dict); if (wordcount <= cmdi + 1) { cli_err ("Invalid Syntax."); gf_log ("cli", GF_LOG_ERROR, "Too less words for snap create command"); goto out; } if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { cli_err ("snapshot create: failed: snapname cannot exceed " "255 characters."); gf_log ("cli", GF_LOG_ERROR, "Snapname too long"); goto out; } snapname = (char *) words[cmdi]; for (i = 0 ; i < strlen (snapname); i++) { /* Following volume name convention */ if (!isalnum (snapname[i]) && (snapname[i] != '_' && (snapname[i] != '-'))) { /* TODO : Is this message enough?? */ cli_err ("Snapname can contain only alphanumeric, " "\"-\" and \"_\" characters"); goto out; } } ret = dict_set_str (dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save snap " "name(%s)", (char *)words[cmdi]); goto out; } /* Filling volume name in the dictionary */ for (i = cmdi + 1 ; i < wordcount && (strcmp (words[i], "description")) != 0 && (strcmp (words[i], "force") != 0) && (strcmp (words[i], "no-timestamp") != 0); i++) { volcount++; /* volume index starts from 1 */ ret = snprintf (key, sizeof (key), "volname%"PRIu64, volcount); if (ret < 0) { goto out; } ret = dict_set_str (dict, key, (char *)words[i]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not " "save volume name(%s)", (char *)words[i]); goto out; } if (i >= cmdi + 2) { ret = -1; cli_err("Creating multiple volume snapshot is not " "supported as of now"); goto out; } /* TODO : remove this above condition check once * multiple volume snapshot is supported */ } if (volcount == 0) { ret = -1; cli_err ("Please provide the volume name"); gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_int32 (dict, "volcount", volcount); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save volcount"); goto out; } /* Verify how we got out of "for" loop, * if it is by reaching wordcount limit then goto "out", * because we need not parse for "description","force" and * "no-timestamp" after this. */ if (i == wordcount) { goto out; } if (strcmp (words[i], "no-timestamp") == 0) { ret = dict_set_str (dict, "no-timestamp", "true"); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save " "time-stamp option"); } if (i == (wordcount-1)) goto out; i++; } if ((strcmp (words[i], "description")) == 0) { ++i; if (i > (wordcount - 1)) { ret = -1; cli_err ("Please provide a description"); gf_log ("cli", GF_LOG_ERROR, "Description not provided"); goto out; } ret = cli_snap_create_desc_parse(dict, words, wordcount, i); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save snap " "description"); goto out; } if (i == (wordcount - 1)) goto out; i++; /* point the index to next word. * As description might be follwed by force option. * Before that, check if wordcount limit is reached */ } if (strcmp (words[i], "force") == 0) { flags = GF_CLI_FLAG_OP_FORCE; } else { ret = -1; cli_err ("Invalid Syntax."); gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } /* Check if the command has anything after "force" keyword */ if (++i < wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = 0; out: if(ret == 0) { /*Adding force flag in either of the case i.e force set * or unset*/ ret = dict_set_int32 (dict, "flags", flags); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save " "snap force option"); } } return ret; } /* snapshot list [volname] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_list_parse (dict_t *dict, const char **words, int wordcount) { int ret = -1; GF_ASSERT (words); GF_ASSERT (dict); if (wordcount < 2 || wordcount > 3) { gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } if (wordcount == 2) { ret = 0; goto out; } ret = dict_set_str (dict, "volname", (char *)words[2]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to save volname in dictionary"); goto out; } out: return ret; } /* snapshot info [(snapname | volume <volname>)] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_info_parse (dict_t *dict, const char **words, int wordcount) { int ret = -1; int32_t cmd = GF_SNAP_INFO_TYPE_ALL; unsigned int cmdi = 2; /* cmdi is command index, here cmdi is "2" (gluster snapshot info)*/ GF_ASSERT (words); GF_ASSERT (dict); if (wordcount > 4 || wordcount < cmdi) { gf_log ("cli", GF_LOG_ERROR, "Invalid syntax"); goto out; } if (wordcount == cmdi) { ret = 0; goto out; } /* If 3rd word is not "volume", then it must * be snapname. */ if (strcmp (words[cmdi], "volume") != 0) { ret = dict_set_str (dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to save " "snapname %s", words[cmdi]); goto out; } /* Once snap name is parsed, if we encounter any other * word then fail it. Invalid Syntax. * example : snapshot info <snapname> word */ if ((cmdi + 1) != wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } cmd = GF_SNAP_INFO_TYPE_SNAP; ret = 0; goto out; /* No need to continue the parsing once we * get the snapname */ } /* If 3rd word is "volume", then check if next word * is present. As, "snapshot info volume" is an * invalid command. */ if ((cmdi + 1) == wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "volname", (char *)words[wordcount - 1]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save " "volume name %s", words[wordcount - 1]); goto out; } cmd = GF_SNAP_INFO_TYPE_VOL; out: if (ret == 0) { ret = dict_set_int32 (dict, "cmd", cmd); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save " "type of snapshot info"); } } return ret; } /* snapshot restore <snapname> * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_restore_parse (dict_t *dict, const char **words, int wordcount, struct cli_state *state) { int ret = -1; const char *question = NULL; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT (words); GF_ASSERT (dict); if (wordcount != 3) { gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "snapname", (char *)words[2]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]); goto out; } question = "Restore operation will replace the " "original volume with the snapshotted volume. " "Do you still want to continue?"; answer = cli_cmd_get_confirmation (state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log ("cli", GF_LOG_ERROR, "User cancelled a snapshot " "restore operation for snap %s", (char *)words[2]); goto out; } out: return ret; } /* snapshot activate <snapname> [force] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_activate_parse (dict_t *dict, const char **words, int wordcount) { int ret = -1; int flags = 0; GF_ASSERT (words); GF_ASSERT (dict); if ((wordcount < 3) || (wordcount > 4)) { gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "snapname", (char *)words[2]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]); goto out; } if (wordcount == 4) { if (!strcmp("force", (char *)words[3])) { flags = GF_CLI_FLAG_OP_FORCE; } else { gf_log ("cli", GF_LOG_ERROR, "Invalid option"); ret = -1; goto out; } } ret = dict_set_int32 (dict, "flags", flags); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to save force option"); goto out; } out: return ret; } /* snapshot deactivate <snapname> * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success * 1 if user cancelled the request */ int cli_snap_deactivate_parse (dict_t *dict, const char **words, int wordcount, struct cli_state *state) { int ret = -1; gf_answer_t answer = GF_ANSWER_NO; const char *question = "Deactivating snap will make its " "data inaccessible. Do you want to " "continue?"; GF_ASSERT (words); GF_ASSERT (dict); if ((wordcount != 3)) { gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "snapname", (char *)words[2]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]); goto out; } answer = cli_cmd_get_confirmation (state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log ("cli", GF_LOG_DEBUG, "User cancelled " "snapshot deactivate operation"); goto out; } out: return ret; } /* snapshot delete (all | snapname | volume <volname>) * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success * 1 if user cancel the operation */ int cli_snap_delete_parse (dict_t *dict, const char **words, int wordcount, struct cli_state *state) { int ret = -1; const char *question = NULL; int32_t cmd = -1; unsigned int cmdi = 2; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT (words); GF_ASSERT (dict); if (wordcount > 4 || wordcount <= cmdi) { gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } question = "Deleting snap will erase all the information about " "the snap. Do you still want to continue?"; if (strcmp (words [cmdi], "all") == 0) { ret = 0; cmd = GF_SNAP_DELETE_TYPE_ALL; } else if (strcmp (words [cmdi], "volume") == 0) { if (++cmdi == wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "volname", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save " "volume name %s", words[wordcount - 1]); goto out; } cmd = GF_SNAP_DELETE_TYPE_VOL; } else { ret = dict_set_str (dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to save " "snapname %s", words[2]); goto out; } cmd = GF_SNAP_DELETE_TYPE_SNAP; } if ((cmdi + 1) != wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } if (cmd == GF_SNAP_DELETE_TYPE_SNAP) { answer = cli_cmd_get_confirmation (state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log ("cli", GF_LOG_DEBUG, "User cancelled " "snapshot delete operation for snap %s", (char *)words[2]); goto out; } } ret = dict_set_int32 (dict, "delete-cmd", cmd); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save " "type of snapshot delete"); } out: return ret; } /* snapshot status [(snapname | volume <volname>)] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_status_parse (dict_t *dict, const char **words, int wordcount) { int ret = -1; int32_t cmd = GF_SNAP_STATUS_TYPE_ALL; unsigned int cmdi = 2; /* cmdi is command index, here cmdi is "2" (gluster snapshot status)*/ GF_ASSERT (words); GF_ASSERT (dict); if (wordcount > 4 || wordcount < cmdi) { gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } if (wordcount == cmdi) { ret = 0; goto out; } /* if 3rd word is not "volume", then it must be "snapname" */ if (strcmp (words[cmdi], "volume") != 0) { ret = dict_set_str (dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Count not save " "snap name %s", words[cmdi]); goto out; } if ((cmdi + 1) != wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = 0; cmd = GF_SNAP_STATUS_TYPE_SNAP; goto out; } /* If 3rd word is "volume", then check if next word is present. * As, "snapshot info volume" is an invalid command */ if ((cmdi + 1) == wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "volname", (char *)words [wordcount - 1]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Count not save " "volume name %s", words[wordcount - 1]); goto out; } cmd = GF_SNAP_STATUS_TYPE_VOL; out: if (ret == 0) { ret = dict_set_int32 (dict, "status-cmd", cmd); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not save cmd " "of snapshot status"); } } return ret; } /* return value: * -1 in case of failure. * 0 in case of success. */ int32_t cli_snap_config_limit_parse (const char **words, dict_t *dict, unsigned int wordcount, unsigned int index, char *key) { int ret = -1; int limit = 0; char *end_ptr = NULL; GF_ASSERT (words); GF_ASSERT (dict); GF_ASSERT (key); if (index >= wordcount) { ret = -1; cli_err ("Please provide a value for %s.", key); gf_log ("cli", GF_LOG_ERROR, "Value not provided for %s", key); goto out; } limit = strtol (words[index], &end_ptr, 10); if (limit <= 0 || strcmp (end_ptr, "") != 0) { ret = -1; cli_err("Please enter an integer value " "greater than zero for %s", key); goto out; } ret = dict_set_int32 (dict, key, limit); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not set " "%s in dictionary", key); goto out; } ret = dict_set_dynstr_with_alloc (dict, "globalname", "All"); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not set global key"); goto out; } ret = dict_set_int32 (dict, "hold_global_locks", _gf_true); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Could not set global locks"); goto out; } out: return ret; } /* function cli_snap_config_parse * Config Syntax : gluster snapshot config [volname] * [snap-max-hard-limit <count>] * [snap-max-soft-limit <count>] * return value: <0 on failure 1 if user cancels the operation, or limit value is out of range 0 on success NOTE : snap-max-soft-limit can only be set for system. */ int32_t cli_snap_config_parse (const char **words, int wordcount, dict_t *dict, struct cli_state *state) { int ret = -1; gf_answer_t answer = GF_ANSWER_NO; gf_boolean_t vol_presence = _gf_false; struct snap_config_opt_vals_ *conf_vals = NULL; int8_t hard_limit = 0; int8_t soft_limit = 0; int8_t config_type = -1; const char *question = NULL; unsigned int cmdi = 2; /* cmdi is command index, here cmdi is "2" (gluster snapshot config)*/ GF_ASSERT (words); GF_ASSERT (dict); GF_ASSERT (state); if ((wordcount < 2) || (wordcount > 7)) { gf_log ("cli", GF_LOG_ERROR, "Invalid wordcount(%d)", wordcount); goto out; } if (wordcount == 2) { config_type = GF_SNAP_CONFIG_DISPLAY; ret = 0; goto set; } /* auto-delete cannot be a volume name */ /* Check whether the 3rd word is volname */ if (strcmp (words[cmdi], "snap-max-hard-limit") != 0 && strcmp (words[cmdi], "snap-max-soft-limit") != 0 && strcmp (words[cmdi], "auto-delete") != 0 && strcmp (words[cmdi], "activate-on-create") != 0) { ret = dict_set_str (dict, "volname", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to set volname"); goto out; } cmdi++; vol_presence = _gf_true; if (cmdi == wordcount) { config_type = GF_SNAP_CONFIG_DISPLAY; ret = 0; goto set; } } config_type = GF_SNAP_CONFIG_TYPE_SET; if (strcmp (words[cmdi], "snap-max-hard-limit") == 0) { ret = cli_snap_config_limit_parse (words, dict, wordcount, ++cmdi, "snap-max-hard-limit"); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " "config hard limit"); goto out; } hard_limit = 1; if (++cmdi == wordcount) { ret = 0; goto set; } } if (strcmp (words[cmdi], "snap-max-soft-limit") == 0) { if (vol_presence == 1) { ret = -1; cli_err ("Soft limit cannot be set to individual " "volumes."); gf_log ("cli", GF_LOG_ERROR, "Soft limit cannot be " "set to volumes"); goto out; } ret = cli_snap_config_limit_parse (words, dict, wordcount, ++cmdi, "snap-max-soft-limit"); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " "config soft limit"); goto out; } if (++cmdi != wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } soft_limit = 1; } if (hard_limit || soft_limit) goto set; if (strcmp(words[cmdi], "auto-delete") == 0) { if (vol_presence == 1) { ret = -1; cli_err ("As of now, auto-delete option cannot be set " "to volumes"); gf_log ("cli", GF_LOG_ERROR, "auto-delete option " "cannot be set to volumes"); goto out; } if (++cmdi >= wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "auto-delete", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to set " "value of auto-delete in request " "dictionary"); goto out; } if (++cmdi != wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } } else if (strcmp(words[cmdi], "activate-on-create") == 0) { if (vol_presence == 1) { ret = -1; cli_err ("As of now, activate-on-create option " "cannot be set to volumes"); gf_log ("cli", GF_LOG_ERROR, "activate-on-create " "option cannot be set to volumes"); goto out; } if (++cmdi >= wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str (dict, "snap-activate-on-create", (char *)words[cmdi]); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to set value " "of activate-on-create in request dictionary"); goto out; } if (++cmdi != wordcount) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } } else { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = 0; /* Success */ set: ret = dict_set_int32 (dict, "config-command", config_type); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to set " "config-command"); goto out; } if (config_type == GF_SNAP_CONFIG_TYPE_SET && (hard_limit || soft_limit)) { conf_vals = snap_confopt_vals; if (hard_limit && soft_limit) { question = conf_vals[GF_SNAP_CONFIG_SET_BOTH].question; } else if (soft_limit) { question = conf_vals[GF_SNAP_CONFIG_SET_SOFT].question; } else if (hard_limit) { question = conf_vals[GF_SNAP_CONFIG_SET_HARD].question; } answer = cli_cmd_get_confirmation (state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log ("cli", GF_LOG_DEBUG, "User cancelled " "snapshot config operation"); } } out: return ret; } int validate_op_name (const char *op, const char *opname, char **opwords) { int ret = -1; int i = 0; GF_ASSERT (opname); GF_ASSERT (opwords); for (i = 0 ; opwords[i] != NULL; i++) { if (strcmp (opwords[i], opname) == 0) { cli_out ("\"%s\" cannot be a %s", opname, op); goto out; } } ret = 0; out: return ret; } int32_t cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options, struct cli_state *state) { int32_t ret = -1; dict_t *dict = NULL; gf1_cli_snapshot type = GF_SNAP_OPTION_TYPE_NONE; char *w = NULL; char *opwords[] = {"create", "delete", "restore", "activate", "deactivate", "list", "status", "config", "info", "clone", NULL}; char *invalid_snapnames[] = {"description", "force", "volume", "all", NULL}; char *invalid_volnames[] = {"volume", "type", "subvolumes", "option", "end-volume", "all", "volume_not_in_ring", "description", "force", "snap-max-hard-limit", "snap-max-soft-limit", "auto-delete", "activate-on-create", NULL}; GF_ASSERT (words); GF_ASSERT (options); GF_ASSERT (state); dict = dict_new (); if (!dict) goto out; /* Lowest wordcount possible */ if (wordcount < 2) { gf_log ("", GF_LOG_ERROR, "Invalid command: Not enough arguments"); goto out; } w = str_getunamb (words[1], opwords); if (!w) { /* Checks if the operation is a valid operation */ gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); goto out; } if (!strcmp (w, "create")) { type = GF_SNAP_OPTION_TYPE_CREATE; } else if (!strcmp (w, "list")) { type = GF_SNAP_OPTION_TYPE_LIST; } else if (!strcmp (w, "info")) { type = GF_SNAP_OPTION_TYPE_INFO; } else if (!strcmp (w, "delete")) { type = GF_SNAP_OPTION_TYPE_DELETE; } else if (!strcmp (w, "config")) { type = GF_SNAP_OPTION_TYPE_CONFIG; } else if (!strcmp (w, "restore")) { type = GF_SNAP_OPTION_TYPE_RESTORE; } else if (!strcmp (w, "status")) { type = GF_SNAP_OPTION_TYPE_STATUS; } else if (!strcmp (w, "activate")) { type = GF_SNAP_OPTION_TYPE_ACTIVATE; } else if (!strcmp (w, "deactivate")) { type = GF_SNAP_OPTION_TYPE_DEACTIVATE; } else if (!strcmp(w, "clone")) { type = GF_SNAP_OPTION_TYPE_CLONE; } if (type != GF_SNAP_OPTION_TYPE_CONFIG && type != GF_SNAP_OPTION_TYPE_STATUS) { ret = dict_set_int32 (dict, "hold_snap_locks", _gf_true); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to set hold-snap-locks value " "as _gf_true"); goto out; } } /* Following commands does not require volume locks */ if (type == GF_SNAP_OPTION_TYPE_STATUS || type == GF_SNAP_OPTION_TYPE_ACTIVATE || type == GF_SNAP_OPTION_TYPE_DEACTIVATE) { ret = dict_set_int32 (dict, "hold_vol_locks", _gf_false); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Setting volume lock " "flag failed"); goto out; } } /* Check which op is intended */ switch (type) { case GF_SNAP_OPTION_TYPE_CREATE: /* Syntax : * gluster snapshot create <snapname> <vol-name(s)> * [no-timestamp] * [description <description>] * [force] */ /* In cases where the snapname is not given then * parsing fails & snapname cannot be "description", * "force" and "volume", that check is made here */ if (wordcount == 2){ ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = validate_op_name ("snapname", words[2], invalid_snapnames); if (ret) { goto out; } ret = cli_snap_create_parse (dict, words, wordcount); if (ret) { gf_log ("cli", GF_LOG_ERROR, "create command parsing failed."); goto out; } break; case GF_SNAP_OPTION_TYPE_CLONE: /* Syntax : * gluster snapshot clone <clonename> <snapname> */ /* In cases where the clonename is not given then * parsing fails & snapname cannot be "description", * "force" and "volume", that check is made here */ if (wordcount == 2) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = validate_op_name ("clonename", words[2], invalid_volnames); if (ret) { goto out; } ret = cli_snap_clone_parse (dict, words, wordcount); if (ret) { gf_log ("cli", GF_LOG_ERROR, "clone command parsing failed."); goto out; } break; case GF_SNAP_OPTION_TYPE_INFO: /* Syntax : * gluster snapshot info [(snapname] | [vol <volname>)] */ ret = cli_snap_info_parse (dict, words, wordcount); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse " "snapshot info command"); goto out; } break; case GF_SNAP_OPTION_TYPE_LIST: /* Syntax : * gluster snaphsot list [volname] */ ret = cli_snap_list_parse (dict, words, wordcount); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse " "snapshot list command"); goto out; } break; case GF_SNAP_OPTION_TYPE_DELETE: /* Syntax : * snapshot delete (all | snapname | volume <volname>) */ ret = cli_snap_delete_parse (dict, words, wordcount, state); if (ret) { /* A positive ret value means user cancelled * the command */ if (ret < 0) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse " "snapshot delete command"); } goto out; } break; case GF_SNAP_OPTION_TYPE_CONFIG: /* snapshot config [volname] [snap-max-hard-limit <count>] * [snap-max-soft-limit <percent>] */ ret = cli_snap_config_parse (words, wordcount, dict, state); if (ret) { if (ret < 0) gf_log ("cli", GF_LOG_ERROR, "config command parsing failed."); goto out; } ret = dict_set_int32 (dict, "type", GF_SNAP_OPTION_TYPE_CONFIG); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Unable to set " "config type"); ret = -1; goto out; } break; case GF_SNAP_OPTION_TYPE_STATUS: { /* Syntax : * gluster snapshot status [(snapname | * volume <volname>)] */ ret = cli_snap_status_parse (dict, words, wordcount); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse " "snapshot status command"); goto out; } break; } case GF_SNAP_OPTION_TYPE_RESTORE: /* Syntax: * snapshot restore <snapname> */ ret = cli_snap_restore_parse (dict, words, wordcount, state); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse " "restore command"); goto out; } break; case GF_SNAP_OPTION_TYPE_ACTIVATE: /* Syntax: * snapshot activate <snapname> [force] */ ret = cli_snap_activate_parse (dict, words, wordcount); if (ret) { gf_log ("cli", GF_LOG_ERROR, "Failed to parse "