GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/publish/cmd_abort.cc
Date: 2025-06-22 02:36:02
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
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 } // namespace
33
34 namespace publish {
35
36 int CmdAbort::Main(const Options &options) {
37 SettingsBuilder builder;
38 const 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 const std::string repository_ident = StripTrailingPath(
50 options.plain_args().empty() ? "" : options.plain_args()[0].value_str);
51 settings = builder.CreateSettingsPublisher(repository_ident,
52 true /* needs_managed */);
53 } catch (const EPublish &e) {
54 if ((e.failure() == EPublish::kFailRepositoryNotFound)
55 || (e.failure() == EPublish::kFailRepositoryType)) {
56 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "CernVM-FS error: %s",
57 e.msg().c_str());
58 return 1;
59 }
60 throw;
61 }
62
63 if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(),
64 false /* temporarily */)) {
65 throw EPublish("No write permission to repository",
66 EPublish::kFailPermission);
67 }
68
69 if (HasPrefix(GetCurrentWorkingDirectory() + "/",
70 settings->transaction().spool_area().union_mnt() + "/",
71 false /* ignore_case */)) {
72 LogCvmfs(kLogCvmfs, kLogStdout,
73 "Current working directory is in %s. Please release, "
74 "e.g. by 'cd $HOME'.",
75 settings->transaction().spool_area().union_mnt().c_str());
76 return 1;
77 }
78
79 if (!options.Has("force")) {
80 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak,
81 "You are about to DISCARD ALL CHANGES OF THE CURRENT TRANSACTION "
82 "for %s! Are you sure (y/N)? ",
83 settings->fqrn().c_str());
84 char answer[] = {0, 0, 0};
85 char *rv_charp = fgets(answer, 3, stdin);
86 if (rv_charp && (answer[0] != 'Y') && (answer[0] != 'y'))
87 return EINTR;
88 } else {
89 // We may have an expired/invalid lease token in the spool area, in which
90 // case dropping the session fails but we still want to continue the
91 // local transaction abort.
92 settings->SetIgnoreInvalidLease(true);
93 }
94
95 std::vector<LsofEntry> lsof_entries = Lsof(
96 settings->transaction().spool_area().union_mnt());
97 if (!lsof_entries.empty()) {
98 if (options.Has("force")) {
99 LogCvmfs(kLogCvmfs, kLogStdout,
100 "WARNING: Open file descriptors on %s (possible race!)"
101 "\nThe following lsof report might show the culprit:\n",
102 settings->transaction().spool_area().union_mnt().c_str());
103 } else {
104 LogCvmfs(kLogCvmfs, kLogStdout,
105 "\nWARNING! There are open read-only file descriptors in %s\n"
106 " --> This is potentially harmful and might cause problems "
107 "later on.\n"
108 " We can anyway perform the requested operation, but this "
109 "will most likely\n"
110 " break other processes with open file descriptors on %s!\n"
111 "\n"
112 " The following lsof report might show the processes with "
113 "open file handles\n",
114 settings->transaction().spool_area().union_mnt().c_str(),
115 settings->transaction().spool_area().union_mnt().c_str());
116 }
117
118 for (unsigned i = 0; i < lsof_entries.size(); ++i) {
119 std::string owner_name;
120 GetUserNameOf(lsof_entries[i].owner, &owner_name);
121 LogCvmfs(kLogCvmfs, kLogStdout, "%s %d %s %s",
122 lsof_entries[i].executable.c_str(), lsof_entries[i].pid,
123 owner_name.c_str(), lsof_entries[i].path.c_str());
124 }
125
126 if (!options.Has("force")) {
127 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak,
128 "\n Do you want to proceed anyway? (y/N) ");
129 char answer[] = {0, 0, 0};
130 char *rv_charp = fgets(answer, 3, stdin);
131 if (rv_charp && (answer[0] != 'Y') && (answer[0] != 'y'))
132 return EINTR;
133 }
134 }
135
136 UniquePtr<Publisher> publisher;
137 publisher = new Publisher(*settings);
138
139 LogCvmfs(kLogCvmfs, kLogSyslog, "(%s) aborting transaction",
140 settings->fqrn().c_str());
141
142 int rvi = CallServerHook("abort_before_hook", settings->fqrn());
143 if (rvi != 0) {
144 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
145 "abort hook failed, not aborting");
146 return rvi;
147 }
148
149 try {
150 publisher->Abort();
151 } catch (const EPublish &e) {
152 if (e.failure() == EPublish::kFailTransactionState) {
153 LogCvmfs(kLogCvmfs, kLogStderr, "%s", e.msg().c_str());
154 return EINVAL;
155 }
156 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "%s", e.msg().c_str());
157 return EIO;
158 }
159
160 rvi = CallServerHook("abort_after_hook", settings->fqrn());
161 if (rvi != 0) {
162 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "post abort hook failed");
163 return rvi;
164 }
165
166 if (settings->transaction().in_enter_session()) {
167 LogCvmfs(kLogCvmfs, kLogStdout,
168 "Discarding changes and closing current transaction...");
169 SafeWriteToFile("abort", session_dir + "/shellaction.marker", 0600);
170 publisher->ExitShell();
171 }
172
173 return 0;
174 }
175
176 } // namespace publish
177