ATLAS Offline Software
Loading...
Searching...
No Matches
OnlineEventDisplaysSvc.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
6#include "Gaudi/Property.h"
7#include "GaudiKernel/IIncidentSvc.h"
8#include "GaudiKernel/Incident.h"
9#include "GaudiKernel/MsgStream.h"
11
12#include <iostream>
13#include <sys/types.h>
14#include <grp.h>
15
16#include <cstdlib> // For std::rand() and std::srand()
17#include <sys/stat.h> //mkdir
18#include <unistd.h> //chown
19#include <stdexcept>
20#include "Python.h"
21
22namespace{
23 template<typename ...Ptr>
24 bool
25 anyNullPtr(Ptr&&...p){
26 return ((p==nullptr) or ...);
27 }
28}
29
31 ISvcLocator* pSvcLocator ) :
32 base_class(name, pSvcLocator){}
33
35
37 if (!evt.isValid()) {
38 ATH_MSG_FATAL("Could not find event info");
39 }
40 std::vector<std::string> streams;
41
42 m_eventNumber = evt->eventNumber();
43 m_runNumber = evt->runNumber();
44
45 //Check what trigger streams were fired, if in list of desired
46 //streams to be reconstructed pick one randomly
47 for (const xAOD::EventInfo::StreamTag& tag : evt->streamTags()){
48 ATH_MSG_DEBUG("A trigger in stream " << tag.type() << "_" << tag.name() << " was fired in this event.");
49 std::string stream_fullname = tag.type() + "_" + tag.name();
50
51 if (m_streamsWanted.empty()) {
52 ATH_MSG_WARNING("You have not requested any specific streams, going to allow all streams");
53 streams.emplace_back(stream_fullname);
54 }
55
56 else{
57 //If the stream is in the list of streams requested, add it
58 if(std::find(m_streamsWanted.begin(), m_streamsWanted.end(), tag.name()) != m_streamsWanted.end()){
59 streams.emplace_back(stream_fullname);
60 }
61 bool isPublic = false;
62 //if the stream is not in the list of public streams wanted, continue
63 if(std::find(m_publicStreams.begin(), m_publicStreams.end(), tag.name()) == m_publicStreams.end()) continue;
64 // Acquire the Global Interpreter Lock (GIL) to ensure thread safety when interacting with Python objects
66 // Convert the project tag string to a Python Unicode object
67 std::string tag = m_projectTag;
68 PyObject* pProjectTag = PyUnicode_FromString(tag.c_str());
69 if (!pProjectTag) {
70 // Error handling: Print Python exception if conversion fails
71 PyErr_Print();
72 ATH_MSG_WARNING("Failed to create Python Unicode object from project tag");
73 } else {
74 // Import the Python module
75 PyObject* pHelper = PyImport_ImportModule("EventDisplaysOnline.EventDisplaysOnlineHelpers");
76 if (!pHelper) {
77 // Error handling: Print Python exception if import fails
78 PyErr_Print();
79 ATH_MSG_WARNING("Failed to import EventDisplaysOnline.EventDisplaysOnlineHelpers module");
80 } else {
81 // Get the "EventCanBeSeenByPublic" function from the module
82 PyObject* EventCanBeSeenByPublic = PyObject_GetAttrString(pHelper, "EventCanBeSeenByPublic");
83 if (!EventCanBeSeenByPublic || !PyCallable_Check(EventCanBeSeenByPublic)) {
84 // Error handling: Print warning if function not found or not callable
85 ATH_MSG_WARNING("Could not find or call EventCanBeSeenByPublic function in EventDisplaysOnline.EventDisplaysOnlineHelpers module");
86 } else {
87 // Call the "EventCanBeSeenByPublic" function with the project tag as argument
88 PyObject* result = PyObject_CallFunctionObjArgs(EventCanBeSeenByPublic, pProjectTag, NULL);
89 if (!result) {
90 PyErr_Print();
91 ATH_MSG_WARNING("Failed to call EventCanBeSeenByPublic function");
92 } else {
93 // Convert the result to a boolean value
94 isPublic = PyObject_IsTrue(result);
95 Py_DECREF(result); // Decrement reference count of the result object
96 }
97 }
98 Py_XDECREF(EventCanBeSeenByPublic);
99 Py_DECREF(pHelper);
100 }
101 Py_DECREF(pProjectTag);
102 }
103 if(isPublic){
104 streams.emplace_back("Public");
105 ATH_MSG_DEBUG("Can send event to public stream");
106 }
107 }
108 }
109 for (const std::string& stream : streams){
110 ATH_MSG_DEBUG("streams where a trigger fired and in your desired streams list: " << stream);
111 }
112 std::random_shuffle(streams.begin(), streams.end());
113 //Pick the first stream as the output directory
114 if(!streams.empty()){
115 m_outputStreamDir = streams[0];
116 }
117 else{
118 ATH_MSG_WARNING("Cannot find a stream adding to .Unknown directory");
119 m_outputStreamDir = ".Unknown";
120 }
121
123 m_FileNamePrefix = m_entireOutputStr + "/JiveXML";;
124
125 gid_t zpgid = setOwnershipToZpGrpOrDefault();
128}
129
132 PyObject* pCheckPair = PyBool_FromLong(m_CheckPair);
133 PyObject* pBeamSplash = PyBool_FromLong(m_BeamSplash);
134 PyObject* pMaxEvents = PyLong_FromLong(m_maxEvents);
135 const char* cString = m_entireOutputStr.c_str();
136 PyObject* pDirectory = PyUnicode_FromString(cString);
137 PyObject* pArgs = PyTuple_Pack(4, pDirectory, pMaxEvents, pCheckPair,pBeamSplash);
138 PyObject* pModule = PyImport_ImportModule(const_cast< char* >("EventDisplaysOnline.EventUtils"));
139 if(!pCheckPair || !pBeamSplash || !pMaxEvents || !pDirectory || !pArgs){
140 PyErr_Print();
141 ATH_MSG_WARNING("Failed to create Python Unicode object");}
142 if (!pModule) {
143 PyErr_Print();
144 ATH_MSG_WARNING("Failed to import EventDisplaysOnline.EventUtils module");
145 } else {
146 ATH_MSG_DEBUG("Successfully imported EventDisplaysOnline.EventUtils module");
147
148 // Get the "cleanDirectory" function from the module
149 PyObject* cleanDirectory = PyObject_GetAttrString(pModule, "cleanDirectory");
150 if (!cleanDirectory || !PyCallable_Check(cleanDirectory)) {
151 ATH_MSG_WARNING("Could not find or call cleanDirectory function in EventDisplaysOnline.EventUtils module");
152 } else {
153 ATH_MSG_DEBUG("Found cleanDirectory function in EventDisplaysOnline.EventUtils module");
154
155 // Call the "cleanDirectory" function with the provided arguments
156 PyObject_CallObject(cleanDirectory, pArgs);
157 if (PyErr_Occurred()) {
158 PyErr_Print();
159 }
160 }
161 if (anyNullPtr(cleanDirectory)){
162 throw std::runtime_error("OnlineEventDisplaysSvc::endEvent: Py_DECREF on nullptr argument");
163 }
164 Py_DECREF(cleanDirectory);
165 }
166
167 if (anyNullPtr(pModule, pArgs, pCheckPair, pMaxEvents, pDirectory)){
168 throw std::runtime_error("OnlineEventDisplaysSvc::endEvent: Py_DECREF on nullptr argument");
169 }
170 Py_DECREF(pModule);
171 Py_DECREF(pArgs);
172 Py_DECREF(pCheckPair);
173 Py_DECREF(pMaxEvents);
174 Py_DECREF(pDirectory);
175}
176
180
184
188void OnlineEventDisplaysSvc::createWriteableDir(const std::string& directory, gid_t zpgid){
189
190 const char* char_dir = directory.c_str();
191 //Time of Check, Time of Use
192 //coverity[TOCTOU]
193 if (access(char_dir, F_OK) == 0) {
194 struct stat directoryStat;
195 //Time of Check, Time of Use
196 //coverity[TOCTOU]
197 if (stat(char_dir, &directoryStat) == 0 && S_ISDIR(directoryStat.st_mode) &&
198 access(char_dir, W_OK) == 0) {
199 ATH_MSG_DEBUG("Going to write file to existing directory: " << directory);
200 if (directoryStat.st_gid != zpgid) {
201 ATH_MSG_DEBUG("Setting group to 'zp' for directory: " << directory);
202 chown(char_dir, -1, zpgid);
203 }
204 } else {
205 ATH_MSG_WARNING("Directory '" << directory << "' is not usable, trying next alternative");
206 }
207 } else {
208 try {
209 auto rc = mkdir(char_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
210 if (rc !=0){
211 ATH_MSG_ERROR("mkdir failed for directory: " << directory);
212 }
213 rc = chown(char_dir, -1, zpgid);
214 if (rc !=0){
215 ATH_MSG_ERROR( "chown failed for directory: " << directory);
216 }
217 ATH_MSG_DEBUG("Created output directory " << directory);
218 } catch (const std::system_error& err) {
219 ATH_MSG_ERROR( "Failed to create output directory " << directory << err.what());
220 }
221 }
222}
223
225 gid_t zpgid;
226 struct group grp;
227 struct group* grp_result;
228 char buf[1024]; // sysconf(_SC_GETGR_R_SIZE_MAX)
229 (void)getgrnam_r("zp", &grp, buf, sizeof(buf), &grp_result);
230 if (grp_result != nullptr) {
231 zpgid = grp.gr_gid;
232 } else {
233 ATH_MSG_DEBUG("If running on private machine, zp group might not exist. Just set to the likely value 1307.");
234 zpgid = 1307;
235 }
236 return zpgid;
237}
238
240
241 ATH_MSG_DEBUG("Initializing " << name());
242 ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", name());
243 ATH_CHECK( incSvc.retrieve() );
244 ATH_MSG_DEBUG("You have requested to only output JiveXML and ESD files when a trigger in the following streams was fired: ");
245 for (const std::string& stream : m_streamsWanted){
246 ATH_MSG_DEBUG(stream);
247 }
248 if(m_BeamSplash){
249 m_CheckPair = false;
250 }
251 incSvc->addListener( this, "BeginEvent");
252 incSvc->addListener( this, "EndEvent");
253
254 ATH_CHECK( m_evt.initialize() );
255
256 return StatusCode::SUCCESS;
257}
258
260
261 ATH_MSG_DEBUG("Finalizing " << name());
262 return StatusCode::SUCCESS;
263}
264
265void OnlineEventDisplaysSvc::handle( const Incident& incident ){
266 ATH_MSG_DEBUG("Received incident " << incident.type() << " from " << incident.source() );
267 if ( incident.type() == IncidentType::BeginEvent && incident.source() == "BeginIncFiringAlg" ){
268 beginEvent();
269 }
270 if (incident.type() == "EndEvent" && incident.source() == "EndIncFiringAlg"){
271 endEvent();
272 }
273}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
_object PyObject
static Double_t rc
Gaudi::Property< std::vector< std::string > > m_streamsWanted
void createWriteableDir(const std::string &directory, gid_t zpgid)
Gaudi::Property< std::string > m_outputDirectory
void handle(const Incident &incident) override
SG::ReadHandleKey< xAOD::EventInfo > m_evt
std::string getEntireOutputStr() override
Gaudi::Property< std::vector< std::string > > m_publicStreams
Gaudi::Property< std::string > m_projectTag
std::string getStreamName() override
Gaudi::Property< bool > m_CheckPair
Gaudi::Property< int > m_maxEvents
Gaudi::Property< bool > m_BeamSplash
std::string getFileNamePrefix() override
StatusCode initialize() override
Class describing a stream tag on the event.