aboutsummaryrefslogtreecommitdiff
blob: 61008a90a77d869c6a1a284d5a503bbe43c2d369 (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
/*
 * virsh-edit.c: Implementation of generic virsh *-edit intelligence
 *
 * Copyright (C) 2012 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Usage:
 * Define macros:
 * EDIT_GET_XML - expression which produces a pointer to XML string, e.g:
 *      #define EDIT_GET_XML virDomainGetXMLDesc(dom, flags)
 *
 * EDIT_NOT_CHANGED - this action is taken if the XML wasn't changed.
 *      Note, that you don't want to jump to cleanup but edit_cleanup label
 *      where temporary variables are free()-d and temporary file is deleted:
 *      #define EDIT_NOT_CHANGED vshPrint(ctl, _("Domain %s XML not changed"), \
 *                                        virDomainGetName(dom)); \
 *                               ret = true; goto edit_cleanup;
 *      Note that this is a statement.
 *
 * EDIT_DEFINE - expression which redefines the object. The edited XML from
 *      user is in 'doc_edited' variable. Don't overwrite the pointer to the
 *      object, as we may iterate once more over and therefore the pointer
 *      would be invalid. Hence assign object to a different variable.
 *      Moreover, this needs to be an expression where:
 *      - 0 is taken as error (our virDefine* APIs often return NULL on error)
 *      - everything else is taken as success
 *      For example:
 *      #define EDIT_DEFINE (dom_edited = virDomainDefineXML(ctl->conn, doc_edited))
 *
 * EDIT_FREE - statement which vir*Free()-s object defined by EDIT_DEFINE, e.g:
 *      #define EDIT_FREE if (dom_edited) virDomainFree(dom_edited);
 *
 * Michal Privoznik <mprivozn@redhat.com>
 */

#ifndef EDIT_GET_XML
# error Missing EDIT_GET_XML definition
#endif

#ifndef EDIT_NOT_CHANGED
# error Missing EDIT_NOT_CHANGED definition
#endif

#ifndef EDIT_DEFINE
# error Missing EDIT_DEFINE definition
#endif

#ifndef EDIT_FREE
# error Missing EDIT_FREE definition
#endif

do {
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;
    const char *msg = NULL;
    bool edit_success = false;

    /* Get the XML configuration of the object. */
    doc = (EDIT_GET_XML);
    if (!doc)
        goto edit_cleanup;

    /* Create and open the temporary file. */
    tmp = vshEditWriteToTempFile(ctl, doc);
    if (!tmp)
        goto edit_cleanup;

reedit:
    /* Start the editor. */
    if (vshEditFile(ctl, tmp) == -1)
        goto edit_cleanup;

    /* Read back the edited file. */
    doc_edited = vshEditReadBackFile(ctl, tmp);
    if (!doc_edited)
        goto edit_cleanup;

    /* Compare original XML with edited.  Has it changed at all? */
    if (STREQ(doc, doc_edited)) {
        EDIT_NOT_CHANGED;
    }

redefine:
    msg = NULL;

    /* Now re-read the object XML.  Did someone else change it while
     * it was being edited?  This also catches problems such as us
     * losing a connection or the object going away.
     */
    doc_reread = (EDIT_GET_XML);
    if (!doc_reread)
        goto edit_cleanup;

    if (STRNEQ(doc, doc_reread)) {
        msg = _("The XML configuration was changed by another user.");
        VIR_FREE(doc);
        doc = doc_reread;
        doc_reread = NULL;
    }

    /* Everything checks out, so redefine the object. */
    EDIT_FREE;
    if (!msg && !(EDIT_DEFINE)) {
        msg = _("Failed.");
    }

    if (msg) {
        int c = vshAskReedit(ctl, msg);
        switch (c) {
        case 'y':
            goto reedit;
            break;

        case 'f':
            goto redefine;
            break;

        case 'n':
            goto edit_cleanup;
            break;

        default:
            vshError(ctl, "%s", msg);
            break;
        }
    }

    edit_success = true;

edit_cleanup:
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);
    if (tmp) {
        unlink (tmp);
        VIR_FREE(tmp);
    }

    if (!edit_success)
        goto cleanup;

} while (0);


#undef EDIT_GET_XML
#undef EDIT_NOT_CHANGED
#undef EDIT_DEFINE
#undef EDIT_FREE