├── CMakeLists.txt ├── COPYING ├── README.md ├── README_tokudb_backup ├── backup └── backup.h └── tokudb_backup.cc /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # disable -Wvla 2 | include(CheckCCompilerFlag) 3 | include(CheckCXXCompilerFlag) 4 | macro(append_cflags_if_supported) 5 | foreach(flag ${ARGN}) 6 | string(REGEX REPLACE "-" "_" temp_flag ${flag}) 7 | check_c_compiler_flag(${flag} HAVE_C_${temp_flag}) 8 | if (HAVE_C_${temp_flag}) 9 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") 10 | endif () 11 | check_cxx_compiler_flag(${flag} HAVE_CXX_${temp_flag}) 12 | if (HAVE_CXX_${temp_flag}) 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") 14 | endif () 15 | endforeach(flag) 16 | endmacro(append_cflags_if_supported) 17 | append_cflags_if_supported(-Wno-vla) 18 | 19 | IF(DEFINED TOKUDB_BACKUP_PLUGIN_VERSION) 20 | ADD_DEFINITIONS("-DTOKUDB_BACKUP_PLUGIN_VERSION=${TOKUDB_BACKUP_PLUGIN_VERSION}") 21 | IF (${TOKUDB_BACKUP_PLUGIN_VERSION} MATCHES "^tokudb-backup-([0-9]+)\\.([0-9]+)") 22 | ADD_DEFINITIONS("-DTOKUDB_BACKUP_PLUGIN_VERSION_MAJOR=${CMAKE_MATCH_1}") 23 | ADD_DEFINITIONS("-DTOKUDB_BACKUP_PLUGIN_VERSION_MINOR=${CMAKE_MATCH_2}") 24 | ENDIF() 25 | ENDIF() 26 | SET(TOKUDB_BACKUP_SOURCES tokudb_backup.cc) 27 | IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/backup/CMakeLists.txt") 28 | ADD_SUBDIRECTORY(backup) 29 | INCLUDE_DIRECTORIES(backup) 30 | MESSAGE(STATUS "tokudb-backup-plugin include backup ${HOT_BACKUP_LIBS}") 31 | set(HOT_BACKUP_LIBS ${HOT_BACKUP_LIBS} PARENT_SCOPE) # export HOT_BACKUP_LIBS to parent 32 | ELSE() 33 | MESSAGE(STATUS "tokudb-backup-plugin no backup ${CMAKE_CURRENT_SOURCE_DIR}") 34 | ENDIF() 35 | MYSQL_ADD_PLUGIN(tokudb_backup ${TOKUDB_BACKUP_SOURCES} MODULE_ONLY MODULE_OUTPUT_NAME "tokudb_backup") 36 | INSTALL(FILES README_tokudb_backup DESTINATION ${INSTALL_DOCDIR}) 37 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Percona TokuBackup library intercepts system calls that write files and duplicates the writes on backup files. It does this while copying files to the backup directory. 2 | 3 | The following technical issues must be addressed to get Percona TokuBackup working with MySQL: 4 | 5 | * The TokuBackup library must be loaded into the mysqld server so that it can intercept system calls. We use LD_PRELOAD to solve the system call intercept problem. Alternatively, the hot backup library can be linked into mysqld. 6 | 7 | * There must be a user interface that can be used to start a backup, track its progress, and determine whether or not the backup succeeded. We use a plugin that kicks off a backup as a side effect of setting a backup session variable to the name of the destination directory. 8 | 9 | # Installing the Percona TokuBackup libraries 10 | 11 | 1 Extract the tarball 12 | 13 | 2 Copy tokudb_backup.so to MySQL's plugin directory 14 | 15 | 3 Copy libHotBackup.so to MySQL's lib directory 16 | 17 | 4 Run mysqld with the TokuBackup library (should exist in the lib directory) 18 | ``` 19 | LD_PRELOAD=PATH_TO_MYSQL_BASE_DIR/lib/libHotBackup.so mysqld_safe 20 | ``` 21 | NOTE: The preload is NOT necessary for MySQL and MariaDB builds from Percona since we link the TokuBackup library into mysqld already. 22 | 23 | 5 Install the TokuBackup plugin (should exist in the MySQL plugin directory) 24 | ``` 25 | install plugin tokudb_backup soname 'tokudb_backup.so'; 26 | ```` 27 | 28 | # Running a backup 29 | 30 | Backup to the '/tmp/backup1047' directory. This blocks until the backup is complete. 31 | ``` 32 | set tokudb_backup_dir='/tmp/backup1047'; 33 | ``` 34 | The ```set tokudb_backup_dir``` statement will succeed if the backup was taken. Otherwise, the ```tokudb_backup_last_error``` variable is set. 35 | 36 | ``` 37 | select @@tokudb_backup_last_error, @@tokudb_backup_last_error_string; 38 | ``` 39 | 40 | # Excluding source files 41 | 42 | Lets suppose that you want to exclude all 'lost+found' directories from the backup. The ```tokudb_backup_exclude``` session variable contains a regular expression that all source file names are compared with. If the source file name matches the exclude regular expression, then the source file is excluded from the backup. 43 | ``` 44 | set tokudb_backup_exclude='/lost\\+found($|/)'; 45 | ``` 46 | ``` 47 | set tokudb_backup_dir='/tmp/backup105'; 48 | ``` 49 | 50 | # Monitoring progress 51 | 52 | Percona TokuBackup updates the processlist state with progress information while it is running. 53 | 54 | # Throttling the backup write rate 55 | 56 | The ```tokudb_backup_throttle``` variable imposes an upper bound on the write rate of the TokuDB backup. Units are bytes per second. Default is no upper bound. 57 | 58 | # Variables 59 | 60 | ## tokudb_backup_plugin_version 61 | * name:tokudb_backup_plugin_version 62 | * readonly:true 63 | * scope:system 64 | * type:str 65 | * comment:version of the TokuBackup plugin 66 | 67 | ## tokudb_backup_version 68 | * name:tokudb_backup_version 69 | * readonly:true 70 | * scope:system 71 | * type:str 72 | * comment:version of the TokuBackup library 73 | 74 | ## tokudb_backup_allowed_prefix 75 | * name:tokudb_backup_allowed_prefix 76 | * readonly:true 77 | * scope:system 78 | * type:str 79 | * comment:allowed prefix of the destination directory 80 | 81 | ## tokudb_backup_throttle 82 | * name:tokudb_backup_throttle 83 | * readonly:false 84 | * scope:session 85 | * type:ulonglong 86 | * def_val:18446744073709551615 87 | * min_val:0 88 | * max_val:18446744073709551615 89 | * comment:backup throttle on write rate in bytes per second 90 | 91 | ## tokudb_backup_dir 92 | * name:tokudb_backup_dir 93 | * readonly:false 94 | * scope:session 95 | * type:str 96 | * comment:name of the directory where the backup is stored 97 | 98 | ## tokudb_backup_last_error 99 | * name:tokudb_backup_last_error 100 | * readonly:false 101 | * scope:session 102 | * type:ulong 103 | * def_val:0 104 | * min_val:0 105 | * max_val:18446744073709551615 106 | * comment:error from the last backup. 0 is success 107 | 108 | ## tokudb_backup_last_error_string 109 | * name:tokudb_backup_last_error_string 110 | * readonly:false 111 | * scope:session 112 | * type:str 113 | * comment:error string of the last backup 114 | 115 | ## tokudb_backup_exclude 116 | * name:tokudb_backup_exclude 117 | * readonly:false 118 | * scope:session 119 | * type:str 120 | * comment:exclude source file regular expression 121 | 122 | # Building the Percona TokuBackup plugin from source 123 | 124 | TokuBackup is now part of Percona Server. For information about building Percona Server, refer to the corresponding documentation at https://www.percona.com/doc/percona-server/5.6/installation.html#compiling-percona-server-from-source. 125 | -------------------------------------------------------------------------------- /README_tokudb_backup: -------------------------------------------------------------------------------- 1 | Quick Installation 2 | 1. Extract the tarball 3 | 2. Copy the tokudb_backup.so to MySQL's lib/mysql/plugin directory 4 | 3. Copy the libHotBackup.so to MySQL's lib directory 5 | 6 | Please see https://github.com/Tokutek/tokudb-backup-plugin for details. 7 | -------------------------------------------------------------------------------- /backup/backup.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: 3 | 4 | #ifndef BACKUP_H 5 | #define BACKUP_H 6 | 7 | #ident "Copyright (c) 2012-2013 Tokutek Inc. All rights reserved." 8 | #ident "$Id: 4edcb140608da110c9a225a606dc13e77301d274 $" 9 | 10 | extern "C" { 11 | 12 | // These public API's should be in C. 13 | 14 | typedef int (*backup_poll_fun_t)(float progress, const char *progress_string, void *poll_extra); 15 | 16 | typedef void (*backup_error_fun_t)(int error_number, const char *error_string, void *error_extra); 17 | 18 | // The exclude_copy callback if called for every file that will be copied. 19 | // When it returns 0, the file is copied. Otherwise, the file copy is skipped. 20 | typedef int (*backup_exclude_copy_fun_t)(const char *source_file,void *extra); 21 | 22 | int tokubackup_create_backup(const char *source_dirs[], const char *dest_dirs[], int dir_count, 23 | backup_poll_fun_t poll_fun, void *poll_extra, 24 | backup_error_fun_t error_fun, void *error_extra, 25 | backup_exclude_copy_fun_t check_fun, void *exclude_copy_extra) 26 | throw() __attribute__((visibility("default"))); 27 | // Effect: Backup the directories in source_dirs into correspnding dest_dirs. 28 | // Periodically call poll_fun. 29 | // If poll_fun returns 0, then the backup continues. 30 | // If poll_fun returns nonzero, then the backup halts, and the result 31 | // returned by poll_fun is returned by tokubackup_create_backup(). 32 | // We pass to the poll_fun 33 | // * a progress number (a float that ranges from 0.0 (no progress so 34 | // far) to 1.0 (done), and 0.5 meaning we think we are half done). 35 | // * a string which the backup software can specify. The string is 36 | // human readable information about the backup progress. The string may 37 | // be deallocated after the poll_fun returns, so if you want to save the 38 | // data, copy it somewhere. 39 | // * the poll_extra which was passed to tokubackup_create_backup(). 40 | // If an error occurs and error_fun is non-NULL then we call 41 | // error_fun with the error number and a string which is descriptive. 42 | // The string may be deallocated soon, so copy it if you want it. 43 | // Arguments: 44 | // source_dirs: an array of strings which name the source directories. 45 | // dest_dirs: an array of strings naming the destinations. 46 | // dir_count: how many source dirs and dest_dirs are there? 47 | // poll_fun: a function to call periodically during backup. 48 | // poll_extra: a value passed to poll_fun. 49 | // error_fun: a function to call if an error happens 50 | // error_extra: a value passed to error_fun. 51 | // exclude_copy_fun: a function to call for every file being copied 52 | // exclude_copy_extra: a valuue passed to the exclude_copy_fun 53 | // Return value: 0 if the backup succeeded. Nonzero if the backup 54 | // was stopped early (returning the result from the poll_fun) or the 55 | // error code. 56 | // Rationale: The poll_fun gives us a way to stop the backup, and also 57 | // to inform the user of progress. 58 | // The error_fun provides a way to give a description (e.g., to put 59 | // into the log). There are thus two ways for the caller to find out the 60 | // error: the error_fun is called and the error number is returned. 61 | // This single function provides almost everything we need to do a 62 | // backup. For example the mysql code can abort backup when the user types ctrl-C 63 | // by returning a nonzero value from poll_fun. 64 | 65 | void tokubackup_throttle_backup(unsigned long bytes_per_second) throw() __attribute__((visibility("default"))); 66 | // Effect: Throttle the rate at which copying happens. 67 | // This function can be called by any thread at any time, and will throttle 68 | // future backups as well as any currently running backup. 69 | // If you pass zero, then the backup will stop consuming any read 70 | // bandwidth on the source directory (but will continue to capture writes 71 | // and put them in the destination). You might want to do that, for example, 72 | // if you have a critical query running and want to absolutely minimize the 73 | // impact of backup, without actually aborting the backup. 74 | // Pass in ULONG_MAX completely unthrottle the backup. (This system may 75 | // actually throttle the backup to that rate, but 76 | // ULONG_MAX comes out to 16 Yobibytes/second, which is effectively 77 | // infinite). 78 | // The system throttles the reads out of the source directory, not writes into 79 | // the destination directory. If the underlying data directory is being modified 80 | // at a high rate, then the destination directory will receive those modifications 81 | // at the same rate, plus receive the throttled read data from the source. 82 | 83 | const extern char *tokubackup_version_string __attribute__((visibility("default"))); 84 | 85 | const int BACKUP_SUCCESS = 0; 86 | } 87 | 88 | #endif // end of header guardian. 89 | -------------------------------------------------------------------------------- /tokudb_backup.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: 3 | #ident "Copyright (c) 2014 Tokutek Inc. All rights reserved." 4 | 5 | #define MYSQL_SERVER 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #if MYSQL_VERSION_ID <= 50599 || defined(MARIADB_BASE_VERSION) 14 | #include // normalize_binlog_name 15 | #else 16 | #include // normalize_binlog_name 17 | #endif 18 | #include // SUPER_ACL 19 | #include // check_global_access 20 | #include "backup/backup.h" 21 | #include 22 | 23 | #ifdef TOKUDB_BACKUP_PLUGIN_VERSION 24 | #define stringify2(x) #x 25 | #define stringify(x) stringify2(x) 26 | #define TOKUDB_BACKUP_PLUGIN_VERSION_STRING stringify(TOKUDB_BACKUP_PLUGIN_VERSION) 27 | #else 28 | #define TOKUDB_BACKUP_PLUGIN_VERSION_STRING NULL 29 | #endif 30 | 31 | static char *tokudb_backup_plugin_version; 32 | 33 | static MYSQL_SYSVAR_STR(plugin_version, tokudb_backup_plugin_version, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "version of the tokudb backup plugin", 34 | NULL, NULL, TOKUDB_BACKUP_PLUGIN_VERSION_STRING); 35 | 36 | static char *tokudb_backup_version = (char *) tokubackup_version_string; 37 | 38 | static MYSQL_SYSVAR_STR(version, tokudb_backup_version, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "version of the tokutek backup library", 39 | NULL, NULL, NULL); 40 | 41 | static MYSQL_THDVAR_ULONG(last_error, PLUGIN_VAR_THDLOCAL, "error from the last backup. 0 is success", 42 | NULL, NULL, 0 /*default*/, 0 /*min*/, ~0ULL /*max*/, 1 /*blocksize*/); 43 | 44 | static MYSQL_THDVAR_STR(last_error_string, PLUGIN_VAR_THDLOCAL + PLUGIN_VAR_MEMALLOC, "error string from the last backup", NULL, NULL, NULL); 45 | 46 | static MYSQL_THDVAR_STR(exclude, PLUGIN_VAR_THDLOCAL + PLUGIN_VAR_MEMALLOC, "exclude source file regular expression", NULL, NULL, NULL); 47 | 48 | static int tokudb_backup_check_dir(THD *thd, struct st_mysql_sys_var *var, void *save, struct st_mysql_value *value); 49 | static void tokudb_backup_update_dir(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save); 50 | 51 | static MYSQL_THDVAR_STR(dir, PLUGIN_VAR_THDLOCAL + PLUGIN_VAR_MEMALLOC, "name of the directory where the backup is stored", tokudb_backup_check_dir, tokudb_backup_update_dir, NULL); 52 | 53 | static int tokudb_backup_check_throttle(THD *thd, struct st_mysql_sys_var *var, void *save, struct st_mysql_value *value); 54 | static void tokudb_backup_update_throttle(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save); 55 | 56 | static MYSQL_THDVAR_ULONGLONG(throttle, PLUGIN_VAR_THDLOCAL, "backup throttle on write rate in bytes per second", 57 | tokudb_backup_check_throttle, tokudb_backup_update_throttle, ~0ULL /*default*/, 0 /*min*/, ~0ULL /*max*/, 1 /*blocksize*/); 58 | 59 | static char *tokudb_backup_allowed_prefix; 60 | 61 | static MYSQL_SYSVAR_STR(allowed_prefix, tokudb_backup_allowed_prefix, PLUGIN_VAR_READONLY, "allowed prefix of the destination directory", 62 | NULL, NULL, NULL); 63 | 64 | static struct st_mysql_sys_var *tokudb_backup_system_variables[] = { 65 | MYSQL_SYSVAR(plugin_version), 66 | MYSQL_SYSVAR(version), 67 | MYSQL_SYSVAR(allowed_prefix), 68 | MYSQL_SYSVAR(throttle), 69 | MYSQL_SYSVAR(dir), 70 | MYSQL_SYSVAR(last_error), 71 | MYSQL_SYSVAR(last_error_string), 72 | MYSQL_SYSVAR(exclude), 73 | NULL, 74 | }; 75 | 76 | struct tokudb_backup_exclude_copy_extra { 77 | THD *_thd; 78 | char *exclude_string; 79 | regex_t *re; 80 | }; 81 | 82 | static int tokudb_backup_exclude_copy_fun(const char *source_file, void *extra) { 83 | tokudb_backup_exclude_copy_extra *exclude_extra = static_cast(extra); 84 | int r = 0; 85 | if (0) fprintf(stderr, "%s %s\n", __FUNCTION__, source_file); 86 | if (exclude_extra->exclude_string) { 87 | int regr = regexec(exclude_extra->re, source_file, 0, NULL, 0); 88 | if (regr == 0) { 89 | if (1) fprintf(stderr, "tokudb backup exclude %s\n", source_file); 90 | r = 1; 91 | } 92 | } 93 | return r; 94 | } 95 | 96 | struct tokudb_backup_progress_extra { 97 | THD *_thd; 98 | char *_the_string; 99 | }; 100 | 101 | static int tokudb_backup_progress_fun(float progress, const char *progress_string, void *extra) { 102 | tokudb_backup_progress_extra *be = static_cast(extra); 103 | 104 | // set thd proc info 105 | thd_proc_info(be->_thd, ""); 106 | size_t len = 100 + strlen(progress_string); 107 | be->_the_string = (char *) my_realloc(be->_the_string, len, MYF(MY_FAE+MY_ALLOW_ZERO_PTR)); 108 | float percentage = progress * 100; 109 | int r = snprintf(be->_the_string, len, "tokudb backup about %.0f%% done: %s", percentage, progress_string); 110 | assert(0 < r && (size_t)r <= len); 111 | thd_proc_info(be->_thd, be->_the_string); 112 | 113 | if (thd_killed(be->_thd)) { 114 | return ER_ABORTING_CONNECTION; 115 | } 116 | return 0; 117 | } 118 | 119 | static void tokudb_backup_set_error(THD *thd, int error, const char *error_string) { 120 | THDVAR(thd, last_error) = error; 121 | char *old_error_string = THDVAR(thd, last_error_string); 122 | THDVAR(thd, last_error_string) = error_string ? my_strdup(error_string, MYF(MY_FAE)) : NULL; 123 | my_free(old_error_string); 124 | } 125 | 126 | static void tokudb_backup_set_error_string(THD *thd, int error, const char *error_fmt, const char *s1, const char *s2, const char *s3) { 127 | size_t n = strlen(error_fmt) + (s1 ? strlen(s1) : 0) + (s2 ? strlen(s2) : 0) + (s3 ? strlen(s3) : 0); 128 | char *error_string = static_cast(my_malloc(n+1, MYF(MY_FAE))); 129 | int r = snprintf(error_string, n+1, error_fmt, s1, s2, s3); 130 | assert(0 < r && (size_t)r <= n); 131 | tokudb_backup_set_error(thd, error, error_string); 132 | my_free(error_string); 133 | } 134 | 135 | struct tokudb_backup_error_extra { 136 | THD *_thd; 137 | }; 138 | 139 | static void tokudb_backup_error_fun(int error_number, const char *error_string, void *extra) { 140 | tokudb_backup_error_extra *be = static_cast(extra); 141 | char *last_error_string = THDVAR(be->_thd, last_error_string); 142 | if (last_error_string == NULL) { 143 | tokudb_backup_set_error(be->_thd, error_number, error_string); 144 | } else { 145 | // append the new error string to the last error string 146 | tokudb_backup_set_error_string(be->_thd, error_number, "%s; %s", last_error_string, error_string, NULL); 147 | } 148 | } 149 | 150 | static char *tokudb_backup_realpath_with_slash(const char *a) { 151 | char *result = NULL; 152 | char *apath = realpath(a, NULL); 153 | if (apath) { 154 | result = apath; 155 | size_t apath_len = strlen(apath); 156 | if (apath[apath_len] != '/') { 157 | char *apath_with_slash = (char *) malloc(apath_len+2); 158 | assert(apath_with_slash); 159 | sprintf(apath_with_slash, "%s/", apath); 160 | free(apath); 161 | result = apath_with_slash; 162 | } 163 | } 164 | return result; 165 | } 166 | 167 | static bool tokudb_backup_is_child_of(const char *a, const char *b) { 168 | bool result = false; 169 | char *apath = tokudb_backup_realpath_with_slash(a); 170 | char *bpath = tokudb_backup_realpath_with_slash(b); 171 | if (apath && bpath) { 172 | result = strncmp(apath, bpath, strlen(bpath)) == 0; 173 | } 174 | if (apath) 175 | free(apath); 176 | if (bpath) 177 | free(bpath); 178 | return result; 179 | } 180 | 181 | const int MYSQL_MAX_DIR_COUNT = 4; 182 | 183 | class source_dirs { 184 | int m_count; 185 | const char *m_dirs[MYSQL_MAX_DIR_COUNT]; 186 | char *m_mysql_data_dir; 187 | const char *m_tokudb_data_dir; 188 | const char *m_tokudb_log_dir; 189 | const char *m_log_bin_dir; 190 | 191 | public: 192 | bool log_bin_set; 193 | bool tokudb_data_set; 194 | bool tokudb_log_set; 195 | 196 | public: 197 | source_dirs() : m_count(0), 198 | m_mysql_data_dir(NULL), 199 | m_tokudb_data_dir(NULL), 200 | m_tokudb_log_dir(NULL), 201 | m_log_bin_dir(NULL), 202 | log_bin_set(false), 203 | tokudb_data_set(false), 204 | tokudb_log_set(false) { 205 | for (int i = 0; i < MYSQL_MAX_DIR_COUNT; ++i) { 206 | m_dirs[i] = NULL; 207 | } 208 | } 209 | 210 | ~source_dirs() { 211 | my_free((void*)m_mysql_data_dir); 212 | my_free((void*)m_tokudb_data_dir); 213 | my_free((void*)m_tokudb_log_dir); 214 | my_free((void*)m_log_bin_dir); 215 | } 216 | 217 | void find_and_allocate_dirs(THD *thd) { 218 | // Sanitize the trailing slash of the MySQL Data Dir. 219 | m_mysql_data_dir = my_strdup(mysql_real_data_home, MYF(MY_FAE)); 220 | #if 0 221 | // These APIs do not exist on MySQL 5.5 or MariaDB. We only need this code if the tokudb storage 222 | // engine is NOT installed. 223 | // To avoid crashes due to my_error being called prematurely by find_plug_in_sys_var, we make sure 224 | // that the tokudb system variables exist which is the case if the tokudb plugin is loaded. 225 | const char *tokudb = "TokuDB"; 226 | LEX_STRING tokudb_string = { (char *) tokudb, strlen(tokudb) }; 227 | lock_plugin_data(); 228 | bool tokudb_found = plugin_find_by_type(&tokudb_string, MYSQL_ANY_PLUGIN) != NULL; 229 | unlock_plugin_data(); 230 | 231 | // Note: These all allocate new strings or return NULL. 232 | if (tokudb_found) { 233 | #endif 234 | m_tokudb_data_dir = this->find_plug_in_sys_var("tokudb_data_dir", thd); 235 | m_tokudb_log_dir = this->find_plug_in_sys_var("tokudb_log_dir", thd); 236 | #if 0 237 | } 238 | #endif 239 | m_log_bin_dir = this->find_log_bin_dir(thd); 240 | } 241 | 242 | bool check_dirs_layout(THD *thd) { 243 | // Ignore directories that are children of the MySQL data dir. 244 | if (m_tokudb_data_dir != NULL && 245 | this->dir_is_child_of_dir(m_tokudb_data_dir, m_mysql_data_dir) == false) { 246 | tokudb_data_set = true; 247 | } 248 | 249 | if (m_tokudb_log_dir != NULL && 250 | this->dir_is_child_of_dir(m_tokudb_log_dir, m_mysql_data_dir) == false) { 251 | tokudb_log_set = true; 252 | } 253 | 254 | if (m_log_bin_dir != NULL && 255 | this->dir_is_child_of_dir(m_log_bin_dir, m_mysql_data_dir) == false) { 256 | log_bin_set = true; 257 | } 258 | 259 | // Check if TokuDB log dir is a child of TokuDB data dir. If it is, we want to ignore it. 260 | if (tokudb_log_set && tokudb_data_set) { 261 | if (this->dir_is_child_of_dir(m_tokudb_log_dir, m_tokudb_data_dir)) { 262 | tokudb_log_set = false; 263 | } 264 | } 265 | 266 | // Check if log bin dir is a child of either TokuDB data dir. 267 | if (log_bin_set && tokudb_data_set) { 268 | if (this->dir_is_child_of_dir(m_log_bin_dir, m_tokudb_data_dir)) { 269 | log_bin_set = false; 270 | } 271 | } 272 | 273 | // Check if log bin dir is a child of either TokuDB log dir. 274 | if (log_bin_set && tokudb_log_set) { 275 | if (this->dir_is_child_of_dir(m_log_bin_dir, m_tokudb_log_dir)) { 276 | log_bin_set = false; 277 | } 278 | } 279 | 280 | // Check if any of the three non-mysql dirs is a strict parent 281 | // of the mysql data dir. This is an error. NOTE: They can 282 | // be the same. 283 | int error = EINVAL; 284 | const char *error_fmt = "%s directory %s can't be a parent of mysql data dir %s when backing up"; 285 | if (tokudb_data_set && 286 | this->dir_is_child_of_dir(m_mysql_data_dir, m_tokudb_data_dir) == true && 287 | this->dirs_are_the_same(m_tokudb_data_dir, m_mysql_data_dir) == false) { 288 | tokudb_backup_set_error_string(thd, error, error_fmt, "tokudb-data-dir", m_tokudb_data_dir, m_mysql_data_dir); 289 | return false; 290 | } 291 | 292 | if (tokudb_log_set && 293 | this->dir_is_child_of_dir(m_mysql_data_dir, m_tokudb_log_dir) == true && 294 | this->dirs_are_the_same(m_tokudb_log_dir, m_mysql_data_dir) == false) { 295 | tokudb_backup_set_error_string(thd, error, error_fmt, "tokudb-log-dir", m_tokudb_log_dir, m_mysql_data_dir); 296 | return false; 297 | } 298 | 299 | if (log_bin_set && 300 | this->dir_is_child_of_dir(m_mysql_data_dir, m_log_bin_dir) == true && 301 | this->dirs_are_the_same(m_log_bin_dir, m_mysql_data_dir) == false) { 302 | tokudb_backup_set_error_string(thd, error, error_fmt, "mysql log-bin", m_log_bin_dir, m_mysql_data_dir); 303 | return false; 304 | } 305 | 306 | return true; 307 | } 308 | 309 | void set_dirs(void) { 310 | // Set the directories in the output array. 311 | m_count = 0; 312 | m_dirs[m_count++] = m_mysql_data_dir; 313 | if (tokudb_data_set) { 314 | m_dirs[m_count++] = m_tokudb_data_dir; 315 | } 316 | if (tokudb_log_set) { 317 | m_dirs[m_count++] = m_tokudb_log_dir; 318 | } 319 | if (log_bin_set) { 320 | m_dirs[m_count++] = m_log_bin_dir; 321 | } 322 | } 323 | 324 | int set_valid_dirs_and_get_count(const char *array[], const int size) { 325 | int count = 0; 326 | if (size > MYSQL_MAX_DIR_COUNT) { 327 | return count; 328 | } 329 | for (int i = 0; i < MYSQL_MAX_DIR_COUNT; ++i) { 330 | if (m_dirs[i] != NULL) { 331 | count++; 332 | } 333 | array[i] = m_dirs[i]; 334 | } 335 | return count; 336 | } 337 | 338 | bool is_child_of_any(const char *dest_dir, THD * thd) { 339 | bool result = false; 340 | for (int i = 0; i < m_count; i++) { 341 | if (tokudb_backup_is_child_of(dest_dir, m_dirs[i])) { 342 | tokudb_backup_set_error_string(thd, EINVAL, "%s is a child of %s", dest_dir, m_dirs[i], NULL); 343 | result = true; 344 | } 345 | } 346 | return result; 347 | } 348 | 349 | private: 350 | 351 | const char * find_log_bin_dir(THD *thd) { 352 | if (opt_bin_logname == NULL) { 353 | return NULL; 354 | } 355 | 356 | // If this has been set to just a filename, and not a path to 357 | // a regular file, we don't want to back this up to its own 358 | // directory, just skip it. 359 | if (opt_bin_logname[0] != '/') { 360 | return NULL; 361 | } 362 | 363 | int length = strlen(opt_bin_logname); 364 | char *buf = (char *)my_malloc(length + 1, 0); 365 | if (buf == NULL) { 366 | return NULL; 367 | } 368 | 369 | bool r = normalize_binlog_name(buf, opt_bin_logname, false); 370 | if (r) { 371 | my_free((void*)buf); 372 | return NULL; 373 | } 374 | 375 | // Add end of string char. 376 | buf[length] = 0; 377 | 378 | // NOTE: We have to extract the directory of this field. 379 | this->truncate_and_set_file_name(buf, length); 380 | return buf; 381 | } 382 | 383 | const char * find_plug_in_sys_var(const char *name, THD *thd) { 384 | const char * result = NULL; 385 | String null_string; 386 | String name_to_find(name, &my_charset_bin); 387 | // If get_system_var fails, it calls my_error 388 | Item *item = get_system_var(thd, 389 | OPT_GLOBAL, 390 | name_to_find.lex_string(), 391 | null_string.lex_string()); 392 | if (item) { 393 | String scratch; 394 | String * str = item->val_str(&scratch); 395 | if (str) { 396 | result = my_strdup(str->ptr(), MYF(MY_FAE)); 397 | } 398 | } 399 | 400 | // delete item; // auto deleted when the query ends 401 | 402 | return result; 403 | } 404 | 405 | // is directory "a" a child of directory "b" 406 | bool dir_is_child_of_dir(const char *a, const char *b) { 407 | return tokudb_backup_is_child_of(a, b); 408 | } 409 | 410 | // is directory "a" the same as directory "b" 411 | bool dirs_are_the_same(const char *a, const char *b) { 412 | bool result = false; 413 | char *apath = tokudb_backup_realpath_with_slash(a); 414 | char *bpath = tokudb_backup_realpath_with_slash(b); 415 | if (apath && bpath) { 416 | result = strcmp(apath, bpath) == 0; 417 | } 418 | if (apath) 419 | free(apath); 420 | if (bpath) 421 | free(bpath); 422 | return result; 423 | } 424 | 425 | // Removes the trailing bin log file from the system variable. 426 | void truncate_and_set_file_name(char *str, int length) { 427 | const char slash = '/'; 428 | int position_of_last_slash = 0; 429 | 430 | // NOTE: We don't care about the leading slash, so it's ok to 431 | // only scan backwards to the 2nd character. 432 | for (int i = length; i > 0; --i) { 433 | if (str[i] == slash) { 434 | position_of_last_slash = i; 435 | break; 436 | } 437 | } 438 | 439 | // NOTE: MySQL should not allow this to happen. The user 440 | // needs to specify a file, not the root dir (/). This 441 | // shouldn't happen, but it might, so let's pretend it's ok. 442 | if (position_of_last_slash != 0) { 443 | // NOTE: We are sanitizing the path by removing the last slash. 444 | str[position_of_last_slash] = 0; 445 | } 446 | } 447 | }; 448 | 449 | struct destination_dirs { 450 | const char * m_backup_dir; 451 | int m_backup_dir_len; 452 | const char * m_dirs[MYSQL_MAX_DIR_COUNT]; 453 | 454 | destination_dirs(const char *backup_dir) : m_backup_dir(backup_dir) { 455 | m_backup_dir_len = strlen(m_backup_dir); 456 | m_dirs[0] = m_backup_dir; 457 | for (int i = 1; i < MYSQL_MAX_DIR_COUNT; ++i) { 458 | m_dirs[i] = NULL; 459 | } 460 | }; 461 | 462 | ~destination_dirs() { 463 | for (int i = 0; i < MYSQL_MAX_DIR_COUNT; ++i) { 464 | my_free((void*)m_dirs[i]); 465 | } 466 | } 467 | 468 | bool set_backup_subdir(const char *postfix, const int index) { 469 | bool result = false; 470 | if (index < 0 || index >= MYSQL_MAX_DIR_COUNT) { 471 | return false; 472 | } 473 | const int len = strlen(postfix); 474 | const int total_len = len + m_backup_dir_len + 1; 475 | char *str = (char *)my_malloc(sizeof(char) * total_len, MYF(0)); 476 | if (str) { 477 | strcpy(str, m_backup_dir); 478 | strcat(str, postfix); 479 | m_dirs[index] = str; 480 | result = true; 481 | } 482 | return result; 483 | }; 484 | 485 | int create_dirs(void) { 486 | int result = 0; 487 | for (int i = 0; i < MYSQL_MAX_DIR_COUNT; ++i) { 488 | if (m_dirs[i]) { 489 | result = my_mkdir(m_dirs[i], 0777, MYF(0)); 490 | if (result != 0) { 491 | result = errno; 492 | break; 493 | } 494 | } 495 | } 496 | return result; 497 | }; 498 | 499 | private: 500 | destination_dirs() {}; 501 | }; 502 | 503 | static void tokudb_backup_run(THD *thd, const char *dest_dir) { 504 | int error = 0; 505 | 506 | // check that the dest dir is a child of the tokudb_backup_allowed_prefix 507 | if (tokudb_backup_allowed_prefix) { 508 | if (!tokudb_backup_is_child_of(dest_dir, tokudb_backup_allowed_prefix)) { 509 | error = EINVAL; 510 | tokudb_backup_set_error_string(thd, error, "%s is not a child of %s", dest_dir, tokudb_backup_allowed_prefix, NULL); 511 | return; 512 | } 513 | } 514 | 515 | // check if the dest dir exists 516 | char *dest_dir_path = tokudb_backup_realpath_with_slash(dest_dir); 517 | if (dest_dir_path == NULL) { 518 | error = errno; 519 | tokudb_backup_set_error_string(thd, error, "Could not get real path for %s", dest_dir, NULL, NULL); 520 | return; 521 | } 522 | free(dest_dir_path); 523 | 524 | struct source_dirs sources; 525 | sources.find_and_allocate_dirs(thd); 526 | 527 | if (sources.check_dirs_layout(thd) == false) { 528 | return; 529 | } 530 | 531 | sources.set_dirs(); 532 | 533 | if (sources.is_child_of_any(dest_dir, thd)) { 534 | return; 535 | } 536 | 537 | struct destination_dirs destinations(dest_dir); 538 | int index = 0; 539 | destinations.set_backup_subdir("/mysql_data_dir", index); 540 | if (sources.tokudb_data_set) { 541 | destinations.set_backup_subdir("/tokudb_data_dir", ++index); 542 | } 543 | 544 | if (sources.tokudb_log_set) { 545 | destinations.set_backup_subdir("/tokudb_log_dir", ++index); 546 | } 547 | 548 | if (sources.log_bin_set) { 549 | destinations.set_backup_subdir("/mysql_log_bin", ++index); 550 | } 551 | 552 | error = destinations.create_dirs(); 553 | if (error) { 554 | tokudb_backup_set_error(thd, error, "tokudb backup couldn't create needed directories."); 555 | return; 556 | } 557 | 558 | char *exclude_string = THDVAR(thd, exclude); 559 | regex_t exclude_re; 560 | if (exclude_string) { 561 | int regr = regcomp(&exclude_re, exclude_string, REG_EXTENDED); 562 | if (regr) { 563 | error = EINVAL; 564 | char reg_error[100+strlen(exclude_string)]; 565 | snprintf(reg_error, sizeof reg_error, "tokudb backup exclude %s regcomp %d", exclude_string, regr); 566 | tokudb_backup_set_error(thd, error, reg_error); 567 | return; 568 | } 569 | } 570 | 571 | const char *source_dirs[MYSQL_MAX_DIR_COUNT] = {}; 572 | const char *dest_dirs[MYSQL_MAX_DIR_COUNT] = {}; 573 | int count = sources.set_valid_dirs_and_get_count(source_dirs, MYSQL_MAX_DIR_COUNT); 574 | for (int i = 0; i < count; ++i) { 575 | dest_dirs[i] = destinations.m_dirs[i]; 576 | } 577 | 578 | // set the throttle 579 | tokubackup_throttle_backup(THDVAR(thd, throttle)); 580 | 581 | // do the backup 582 | tokudb_backup_progress_extra progress_extra = { thd, NULL }; 583 | tokudb_backup_error_extra error_extra = { thd }; 584 | tokudb_backup_exclude_copy_extra exclude_copy_extra = { thd, exclude_string, &exclude_re }; 585 | error = tokubackup_create_backup(source_dirs, dest_dirs, count, 586 | tokudb_backup_progress_fun, &progress_extra, 587 | tokudb_backup_error_fun, &error_extra, 588 | tokudb_backup_exclude_copy_fun, &exclude_copy_extra); 589 | 590 | if (exclude_string) 591 | regfree(&exclude_re); 592 | 593 | // cleanup 594 | thd_proc_info(thd, "tokudb backup done"); // must be a static string 595 | my_free(progress_extra._the_string); 596 | 597 | THDVAR(thd, last_error) = error; 598 | } 599 | 600 | static int tokudb_backup_check_dir(THD *thd, struct st_mysql_sys_var *var, void *save, struct st_mysql_value *value) { 601 | // check for set global and its synomyms 602 | 603 | // reset error variables 604 | int error = 0; 605 | tokudb_backup_set_error(thd, error, NULL); 606 | 607 | // check access 608 | if (check_global_access(thd, SUPER_ACL)) { 609 | return 1; 610 | } 611 | 612 | // check_func_str 613 | char buff[STRING_BUFFER_USUAL_SIZE]; 614 | int length = sizeof(buff); 615 | const char *str = value->val_str(value, buff, &length); 616 | if (str) { 617 | str = thd->strmake(str, length); 618 | *(const char**)save = str; 619 | } 620 | 621 | if (str) { 622 | // run backup 623 | tokudb_backup_run(thd, str); 624 | 625 | // get the last backup error 626 | error = THDVAR(thd, last_error); 627 | } else { 628 | error = EINVAL; 629 | } 630 | 631 | return error; 632 | } 633 | 634 | static void tokudb_backup_update_dir(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { 635 | // nothing to do, backup is run in the check dir function 636 | } 637 | 638 | static int tokudb_backup_check_throttle(THD *thd, struct st_mysql_sys_var *var, void *save, struct st_mysql_value *value) { 639 | // check access 640 | if (check_global_access(thd, SUPER_ACL)) { 641 | return 1; 642 | } 643 | 644 | // save throttle 645 | longlong n; 646 | value->val_int(value, &n); 647 | *(longlong *) save = n; 648 | return 0; 649 | } 650 | 651 | static void tokudb_backup_update_throttle(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { 652 | my_ulonglong *val = (my_ulonglong *) var_ptr; 653 | *val = *(my_ulonglong*) save; 654 | unsigned long nb = *val; 655 | tokubackup_throttle_backup(nb); 656 | } 657 | 658 | static int tokudb_backup_plugin_init(void *p) { 659 | DBUG_ENTER(__FUNCTION__); 660 | DBUG_RETURN(0); 661 | } 662 | 663 | static int tokudb_backup_plugin_deinit(void *p) { 664 | DBUG_ENTER(__FUNCTION__); 665 | DBUG_RETURN(0); 666 | } 667 | 668 | struct st_mysql_daemon tokudb_backup_plugin = { 669 | MYSQL_DAEMON_INTERFACE_VERSION 670 | }; 671 | 672 | #ifndef TOKUDB_BACKUP_PLUGIN_VERSION_MAJOR 673 | #define TOKUDB_BACKUP_PLUGIN_VERSION_MAJOR 0 674 | #endif 675 | #ifndef TOKUDB_BACKUP_PLUGIN_VERSION_MINOR 676 | #define TOKUDB_BACKUP_PLUGIN_VERSION_MINOR 0 677 | #endif 678 | 679 | mysql_declare_plugin(tokudb_backup) { 680 | MYSQL_DAEMON_PLUGIN, 681 | &tokudb_backup_plugin, 682 | "tokudb_backup", 683 | "Tokutek", 684 | "Tokutek hot backup", 685 | PLUGIN_LICENSE_GPL, 686 | tokudb_backup_plugin_init, // Plugin Init 687 | tokudb_backup_plugin_deinit, // Plugin Deinit 688 | (TOKUDB_BACKUP_PLUGIN_VERSION_MAJOR << 8) + TOKUDB_BACKUP_PLUGIN_VERSION_MINOR, 689 | NULL, // status variables 690 | tokudb_backup_system_variables, // system variables 691 | NULL, // config options 692 | 0, // flags 693 | } 694 | mysql_declare_plugin_end; 695 | --------------------------------------------------------------------------------