blob: 8a076f2d73628d45f33522907638ccd0416285a0 [file] [log] [blame]
Patrick Venturef085d912019-03-15 08:50:00 -07001/*
2 * Copyright 2019 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "handler.hpp"
18
Patrick Ventured2037c62019-03-15 10:29:47 -070019#include "errors.hpp"
20
21#include <ipmid/api.h>
22
Patrick Venturebb90d4f2019-03-15 13:42:06 -070023#include <cinttypes>
Patrick Ventured2037c62019-03-15 10:29:47 -070024#include <cstdio>
25#include <filesystem>
26#include <fstream>
Patrick Ventureaa374122019-03-15 15:09:10 -070027#include <phosphor-logging/log.hpp>
28#include <sdbusplus/bus.hpp>
Patrick Ventured2037c62019-03-15 10:29:47 -070029#include <sstream>
30#include <string>
31#include <tuple>
32
Patrick Venturef085d912019-03-15 08:50:00 -070033// The phosphor-host-ipmi daemon requires a configuration that maps
34// the if_name to the IPMI LAN channel. However, that doesn't strictly
35// define which is meant to be used for NCSI.
36#ifndef NCSI_IPMI_CHANNEL
37#define NCSI_IPMI_CHANNEL 1
38#endif
39
40#ifndef NCSI_IF_NAME
41#define NCSI_IF_NAME eth0
42#endif
43
44// To deal with receiving a string without quotes.
45#define QUOTE(name) #name
46#define STR(macro) QUOTE(macro)
47#define NCSI_IF_NAME_STR STR(NCSI_IF_NAME)
48
49namespace google
50{
51namespace ipmi
52{
Patrick Ventured2037c62019-03-15 10:29:47 -070053namespace fs = std::filesystem;
Patrick Venturef085d912019-03-15 08:50:00 -070054
55std::tuple<std::uint8_t, std::string> Handler::getEthDetails() const
56{
57 return std::make_tuple(NCSI_IPMI_CHANNEL, NCSI_IF_NAME_STR);
58}
59
Patrick Ventured2037c62019-03-15 10:29:47 -070060std::int64_t Handler::getRxPackets(const std::string& name) const
61{
62 std::ostringstream opath;
63 opath << "/sys/class/net/" << name << "/statistics/rx_packets";
64 std::string path = opath.str();
65
66 // Minor sanity & security check (of course, I'm less certain if unicode
67 // comes into play here.
68 //
69 // Basically you can't easily inject ../ or /../ into the path below.
70 if (name.find("/") != std::string::npos)
71 {
72 std::fprintf(stderr, "Invalid or illegal name: '%s'\n", name.c_str());
73 throw IpmiException(IPMI_CC_INVALID_FIELD_REQUEST);
74 }
75
76 std::error_code ec;
77 if (!fs::exists(path, ec))
78 {
79 std::fprintf(stderr, "Path: '%s' doesn't exist.\n", path.c_str());
80 throw IpmiException(IPMI_CC_INVALID_FIELD_REQUEST);
81 }
82 // We're uninterested in the state of ec.
83
84 int64_t count = 0;
85 std::ifstream ifs;
86 ifs.exceptions(std::ifstream::failbit);
87 try
88 {
89 ifs.open(path);
90 ifs >> count;
91 }
92 catch (std::ios_base::failure& fail)
93 {
94 throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
95 }
96
97 return count;
98}
99
Patrick Venturebb90d4f2019-03-15 13:42:06 -0700100VersionTuple Handler::getCpldVersion(unsigned int id) const
101{
102 std::ostringstream opath;
103 opath << "/run/cpld" << id << ".version";
104 // Check for file
105
106 std::error_code ec;
107 if (!fs::exists(opath.str(), ec))
108 {
109 std::fprintf(stderr, "Path: '%s' doesn't exist.\n",
110 opath.str().c_str());
111 throw IpmiException(IPMI_CC_INVALID_FIELD_REQUEST);
112 }
113 // We're uninterested in the state of ec.
114
115 // If file exists, read.
116 std::ifstream ifs;
117 ifs.exceptions(std::ifstream::failbit);
118 std::string value;
119 try
120 {
121 ifs.open(opath.str());
122 ifs >> value;
123 }
124 catch (std::ios_base::failure& fail)
125 {
126 throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
127 }
128
129 // If value parses as expected, return version.
130 VersionTuple version = std::make_tuple(0, 0, 0, 0);
131
132 int num_fields =
133 std::sscanf(value.c_str(), "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8,
134 &std::get<0>(version), &std::get<1>(version),
135 &std::get<2>(version), &std::get<3>(version));
136 if (num_fields == 0)
137 {
138 std::fprintf(stderr, "Invalid version.\n");
139 throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
140 }
141
142 return version;
143}
144
Patrick Ventureaa374122019-03-15 15:09:10 -0700145static constexpr auto TIME_DELAY_FILENAME = "/run/psu_timedelay";
146static constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
147static constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
148static constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
149static constexpr auto PSU_HARDRESET_TARGET = "gbmc-psu-hardreset.target";
150
151void Handler::psuResetDelay(std::uint32_t delay) const
152{
153 using namespace phosphor::logging;
154
155 std::ofstream ofs;
156 ofs.open(TIME_DELAY_FILENAME, std::ofstream::out);
157 if (!ofs.good())
158 {
159 std::fprintf(stderr, "Unable to open file for output.\n");
160 throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
161 }
162
163 ofs << "PSU_HARDRESET_DELAY=" << delay << std::endl;
164 if (ofs.fail())
165 {
166 std::fprintf(stderr, "Write failed\n");
167 ofs.close();
168 throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
169 }
170
171 // Write succeeded, please continue.
172 ofs.flush();
173 ofs.close();
174
175 auto bus = sdbusplus::bus::new_default();
176 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
177 SYSTEMD_INTERFACE, "StartUnit");
178
179 method.append(PSU_HARDRESET_TARGET);
180 method.append("replace");
181
182 try
183 {
184 bus.call_noreply(method);
185 }
186 catch (const sdbusplus::exception::SdBusError& ex)
187 {
188 log<level::ERR>("Failed to call PSU hard reset");
189 throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
190 }
191}
192
Patrick Venturef085d912019-03-15 08:50:00 -0700193Handler handlerImpl;
194
195} // namespace ipmi
196} // namespace google