KiCad PCB EDA Suite
kicad_curl.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015 Mark Roszko <mark.roszko@gmail.com>
5  * Copyright (C) 2016 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 // kicad_curl.h must be included before wx headers, to avoid
27 // conflicts for some defines, at least on Windows
28 #include <kicad_curl/kicad_curl.h>
29 
30 #include <wx/log.h>
31 #include <wx/dynlib.h>
32 
33 #include <macros.h>
34 #include <fctsys.h>
35 #include <ki_mutex.h> // MUTEX and MUTLOCK
36 #include <richio.h>
37 
38 
39 
40 // These are even more private than class members, and since there is only
41 // one instance of KICAD_CURL ever, these statics are hidden here to simplify the
42 // client (API) header file.
43 static volatile bool s_initialized;
44 
45 static MUTEX s_lock; // for s_initialized
46 
47 // Assume that on these platforms libcurl uses OpenSSL
48 #if defined(__linux__) || defined(__MINGW32__)
49 
50 #include <openssl/crypto.h>
51 
52 static MUTEX* s_crypto_locks;
53 
54 static void lock_callback( int mode, int type, const char* file, int line )
55 {
56  (void)file;
57  (void)line;
58 
59  wxASSERT( s_crypto_locks && unsigned( type ) < unsigned( CRYPTO_num_locks() ) );
60 
61  //DBG( printf( "%s: mode=0x%x type=%d file=%s line=%d\n", __func__, mode, type, file, line );)
62 
63  if( mode & CRYPTO_LOCK )
64  {
65  s_crypto_locks[ type ].lock();
66  }
67  else
68  {
69  s_crypto_locks[ type ].unlock();
70  }
71 }
72 
73 
74 static void init_locks()
75 {
76  s_crypto_locks = new MUTEX[ CRYPTO_num_locks() ];
77 
78  // From http://linux.die.net/man/3/crypto_set_id_callback:
79 
80  /*
81 
82  OpenSSL can safely be used in multi-threaded applications provided that at
83  least two callback functions are set, locking_function and threadid_func.
84 
85  locking_function(int mode, int n, const char *file, int line) is needed to
86  perform locking on shared data structures. (Note that OpenSSL uses a number
87  of global data structures that will be implicitly shared whenever multiple
88  threads use OpenSSL.) Multi-threaded applications will crash at random if it
89  is not set.
90 
91  threadid_func( CRYPTO_THREADID *id) is needed to record the
92  currently-executing thread's identifier into id. The implementation of this
93  callback should not fill in id directly, but should use
94  CRYPTO_THREADID_set_numeric() if thread IDs are numeric, or
95  CRYPTO_THREADID_set_pointer() if they are pointer-based. If the application
96  does not register such a callback using CRYPTO_THREADID_set_callback(), then
97  a default implementation is used - on Windows and BeOS this uses the
98  system's default thread identifying APIs, and on all other platforms it uses
99  the address of errno. The latter is satisfactory for thread-safety if and
100  only if the platform has a thread-local error number facility.
101 
102  Dick: "sounds like CRYPTO_THREADID_set_callback() is not mandatory on our
103  2 OpenSSL platforms."
104 
105  */
106 
107  CRYPTO_set_locking_callback( &lock_callback );
108 }
109 
110 
111 static void kill_locks()
112 {
113  CRYPTO_set_locking_callback( NULL );
114 
115  delete[] s_crypto_locks;
116 
117  s_crypto_locks = NULL;
118 }
119 
120 #else
121 
122 inline void init_locks() { /* dummy */ }
123 inline void kill_locks() { /* dummy */ }
124 
125 #endif
126 
129 static void at_terminate()
130 {
132 }
133 
134 
136 {
137  // We test s_initialized twice in an effort to avoid
138  // unnecessarily locking s_lock. This understands that the common case
139  // will not need to lock.
140  if( !s_initialized )
141  {
142  MUTLOCK lock( s_lock );
143 
144  if( !s_initialized )
145  {
146  if( curl_global_init( CURL_GLOBAL_ALL ) != CURLE_OK )
147  {
148  THROW_IO_ERROR( "curl_global_init() failed." );
149  }
150 
151  init_locks();
152 
153  wxLogDebug( "Using %s", GetVersion() );
154 
155  s_initialized = true;
156  }
157  }
158 }
159 
160 
162 {
163  /*
164 
165  Calling MUTLOCK() from a static destructor will typically be bad, since the
166  s_lock may already have been statically destroyed itself leading to a boost
167  exception. (Remember C++ does not provide certain sequencing of static
168  destructor invocation.)
169 
170  To prevent this we test s_initialized twice, which ensures that the MUTLOCK
171  is only instantiated on the first call, which should be from
172  PGM_BASE::destroy() which is first called earlier than static destruction.
173  Then when called again from the actual PGM_BASE::~PGM_BASE() function,
174  MUTLOCK will not be instantiated because s_initialized will be false.
175 
176  */
177 
178  if( s_initialized )
179  {
180  MUTLOCK lock( s_lock );
181 
182  if( s_initialized )
183  {
184  curl_global_cleanup();
185 
186  kill_locks();
187 
188  atexit( &at_terminate );
189 
190  s_initialized = false;
191  }
192  }
193 }
194 
195 
197 {
198  if( !s_initialized )
199  Init();
200 
201  curl_version_info_data* info = curl_version_info( CURLVERSION_NOW );
202 
203  std::string res;
204 
205  if( info->version )
206  {
207  res += "libcurl version: " + std::string( info->version );
208  }
209 
210  res += " (";
211 
212  if( info->features & CURL_VERSION_SSL )
213  {
214  res += "with SSL - ";
215  res += std::string( info->ssl_version );
216  }
217  else
218  {
219  res += "without SSL";
220  }
221  res += ")";
222 
223  return res;
224 }
static void Cleanup()
Function Cleanup calls curl_global_cleanup for the application.
Definition: kicad_curl.cpp:161
static void Init()
Function Init calls curl_global_init for the application.
Definition: kicad_curl.cpp:135
boost::interprocess::interprocess_mutex MUTEX
Establish KiCad MUTEX choices here in this file: typedef MUTEX and typedef MUTLOCK.
Definition: ki_mutex.h:42
This file contains miscellaneous commonly used macros and functions.
static void at_terminate()
At process termination, using atexit() keeps the CURL stuff out of the singletops and PGM_BASE...
Definition: kicad_curl.cpp:129
static volatile bool s_initialized
Definition: kicad_curl.cpp:43
void kill_locks()
Definition: kicad_curl.cpp:123
boost::interprocess::scoped_lock< MUTEX > MUTLOCK
Definition: ki_mutex.h:43
static const char * GetVersion()
Function GetVersion wrapper for curl_version().
Definition: kicad_curl.h:93
void init_locks()
Definition: kicad_curl.cpp:122
static MUTEX s_lock
Definition: kicad_curl.cpp:45
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
static std::string GetSimpleVersion()
Function GetSimpleVersion Reports back curl version only and SSL library support. ...
Definition: kicad_curl.cpp:196