GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/publish/cmd_abort.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 0 71 0.0%
Branches: 0 202 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "cvmfs_config.h"
6 #include "cmd_abort.h"
7
8 #include <unistd.h>
9
10 #include <cstdio>
11 #include <string>
12
13 #include "publish/cmd_util.h"
14 #include "publish/except.h"
15 #include "publish/repository.h"
16 #include "publish/settings.h"
17 #include "util/logging.h"
18 #include "util/pointer.h"
19 #include "util/posix.h"
20 #include "util/string.h"
21 #include "whitelist.h"
22
23 namespace {
24 std::string StripTrailingPath(const std::string& repo_and_path) {
25 if (!repo_and_path.empty()) {
26 std::vector<std::string> tokens = SplitString(repo_and_path, '/');
27 return tokens[0];
28 }
29
30 return "";
31 }
32 }
33
34 namespace publish {
35
36 int CmdAbort::Main(const Options &options) {
37 SettingsBuilder builder;
38 std::string session_dir = Env::GetEnterSessionDir();
39
40 if (!session_dir.empty()) {
41 builder.SetConfigPath(session_dir);
42 }
43
44 UniquePtr<SettingsPublisher> settings;
45 try {
46 // Legacy behaviour is that trailing paths after the repository name should
47 // be ignored, e.g. cvmfs_server abort repo.cern.ch/some/path is equivalent
48 // to cvmfs_server abort repo.cern.ch
49 std::string repository_ident = StripTrailingPath(
50 options.plain_args().empty() ? "" : options.plain_args()[0].value_str);
51 settings = builder.CreateSettingsPublisher(
52 repository_ident, true /* needs_managed */);
53 } catch (const EPublish &e) {
54 if ((e.failure() == EPublish::kFailRepositoryNotFound) ||
55 (e.failure() == EPublish::kFailRepositoryType))
56 {
57 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "CernVM-FS error: %s",
58 e.msg().c_str());
59 return 1;
60 }
61 throw;
62 }
63
64 if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(),
65 false /* temporarily */))
66 {
67 throw EPublish("No write permission to repository",
68 EPublish::kFailPermission);
69 }
70
71 if (HasPrefix(GetCurrentWorkingDirectory() + "/",
72 settings->transaction().spool_area().union_mnt() + "/",
73 false /* ignore_case */))
74 {
75 LogCvmfs(kLogCvmfs, kLogStdout,
76 "Current working directory is in %s. Please release, "
77 "e.g. by 'cd $HOME'.",
78 settings->transaction().spool_area().union_mnt().c_str());
79 return 1;
80 }
81
82 if (!options.Has("force")) {
83 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak,
84 "You are about to DISCARD ALL CHANGES OF THE CURRENT TRANSACTION "
85 "for %s! Are you sure (y/N)? ", settings->fqrn().c_str());
86 char answer[] = {0, 0, 0};
87 char *rv_charp = fgets(answer, 3, stdin);
88 if (rv_charp && (answer[0] != 'Y') && (answer[0] != 'y'))
89 return EINTR;
90 } else {
91 // We may have an expired/invalid lease token in the spool area, in which
92 // case dropping the session fails but we still want to continue the
93 // local transaction abort.
94 settings->SetIgnoreInvalidLease(true);
95 }
96
97 std::vector<LsofEntry> lsof_entries =
98 Lsof(settings->transaction().spool_area().union_mnt());
99 if (!lsof_entries.empty()) {
100 if (options.Has("force")) {
101 LogCvmfs(kLogCvmfs, kLogStdout,
102 "WARNING: Open file descriptors on %s (possible race!)"
103 "\nThe following lsof report might show the culprit:\n",
104 settings->transaction().spool_area().union_mnt().c_str());
105 } else {
106 LogCvmfs(kLogCvmfs, kLogStdout,
107 "\nWARNING! There are open read-only file descriptors in %s\n"
108 " --> This is potentially harmful and might cause problems "
109 "later on.\n"
110 " We can anyway perform the requested operation, but this "
111 "will most likely\n"
112 " break other processes with open file descriptors on %s!\n"
113 "\n"
114 " The following lsof report might show the processes with "
115 "open file handles\n",
116 settings->transaction().spool_area().union_mnt().c_str(),
117 settings->transaction().spool_area().union_mnt().c_str());
118 }
119
120 for (unsigned i = 0; i < lsof_entries.size(); ++i) {
121 std::string owner_name;
122 GetUserNameOf(lsof_entries[i].owner, &owner_name);
123 LogCvmfs(kLogCvmfs, kLogStdout, "%s %d %s %s",
124 lsof_entries[i].executable.c_str(),
125 lsof_entries[i].pid,
126 owner_name.c_str(),
127 lsof_entries[i].path.c_str());
128 }
129
130 if (!options.Has("force")) {
131 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak,
132 "\n Do you want to proceed anyway? (y/N) ");
133 char answer[] = {0, 0, 0};
134 char *rv_charp = fgets(answer, 3, stdin);
135 if (rv_charp && (answer[0] != 'Y') && (answer[0] != 'y'))
136 return EINTR;
137 }
138 }
139
140 UniquePtr<Publisher> publisher;
141 publisher = new Publisher(*settings);
142
143 LogCvmfs(kLogCvmfs, kLogSyslog, "(%s) aborting transaction",
144 settings->fqrn().c_str());
145
146 int rvi = CallServerHook("abort_before_hook", settings->fqrn());
147 if (rvi != 0) {
148 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
149 "abort hook failed, not aborting");
150 return rvi;
151 }
152
153 try {
154 publisher->Abort();
155 } catch (const EPublish &e) {
156 if (e.failure() == EPublish::kFailTransactionState) {
157 LogCvmfs(kLogCvmfs, kLogStderr, "%s", e.msg().c_str());
158 return EINVAL;
159 }
160 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "%s", e.msg().c_str());
161 return EIO;
162 }
163
164 rvi = CallServerHook("abort_after_hook", settings->fqrn());
165 if (rvi != 0) {
166 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
167 "post abort hook failed");
168 return rvi;
169 }
170
171 if (settings->transaction().in_enter_session()) {
172 LogCvmfs(kLogCvmfs, kLogStdout,
173 "Discarding changes and closing current transaction...");
174 SafeWriteToFile("abort", session_dir + "/shellaction.marker", 0600);
175 publisher->ExitShell();
176 }
177
178 return 0;
179 }
180
181 } // namespace publish
182