├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── Makefile ├── README.md ├── configurePath.sh ├── mbpfan.8.gz ├── mbpfan.conf ├── mbpfan.conf.test0 ├── mbpfan.conf.test1 ├── mbpfan.conf.test2 ├── mbpfan.init.debian ├── mbpfan.init.gentoo ├── mbpfan.init.redhat ├── mbpfan.service ├── mbpfan.spec ├── mbpfan.upstart ├── src ├── daemon.c ├── daemon.h ├── global.h ├── main.c ├── mbpfan.c ├── mbpfan.h ├── settings.c ├── settings.h ├── strmap.c ├── strmap.h ├── util.c └── util.h └── tests ├── main.c ├── minunit.c └── minunit.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij 2 | .idea 3 | *.iml 4 | 5 | # Eclipse 6 | .cproject 7 | .project 8 | 9 | # Object files 10 | *.o 11 | bin/* 12 | 13 | # Libraries 14 | *.lib 15 | 16 | # Shared objects (inc. Windows DLLs) 17 | *.dll 18 | *.so 19 | 20 | # Executables 21 | *.exe 22 | *.out 23 | 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: c 3 | script: 4 | - make 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | AUTHORS, CONTRIBUTORS, MAINTAINERS 2 | ---------------------------------- 3 | 4 | Daniel Graziotin 5 | 6 | Marji Cermak 7 | John Ferlito 8 | Andrew Gaul 9 | Ismail Khatib 10 | Trevor Joynson 11 | Magnus Stubman 12 | Olivier Tilmans 13 | Yamakaky 14 | Yi Yang 15 | Herminio Hernandez Jr 16 | Robert Musial 17 | Ati Sharma 18 | 19 | 20 | ORIGINARY AUTHORS 21 | ----------------- 22 | Allan McRae mbpfan 23 | rvega 24 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | COMPILER=cc 2 | 3 | C = c 4 | OBJ = o 5 | OUTPUT_PATH = bin/ 6 | SOURCE_PATH = src/ 7 | TESTS_PATH = tests/ 8 | TESTS_BIN = bin/mbpfan-tests 9 | BIN = bin/mbpfan 10 | CONF = mbpfan.conf 11 | DOC = README.md 12 | MAN = mbpfan.8.gz 13 | 14 | COPT = 15 | CC ?= cc 16 | OBJFLAG = -o 17 | BINFLAG = -o 18 | INCLUDES = 19 | LIBS = -lm 20 | LIBPATH = 21 | CFLAGS += $(COPT) -g $(INCLUDES) -Wall -Wextra -Wno-unused-function -std=c99 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500 22 | LDFLAGS += $(LIBPATH) -g 23 | 24 | OBJS := $(patsubst %.$(C),%.$(OBJ),$(wildcard $(SOURCE_PATH)*.$(C))) 25 | TESTS_OBJS := $(patsubst %.$(C),%.$(OBJ),$(wildcard $(TESTS_PATH)*.$(C))) 26 | TESTS_OBJS += $(filter-out %main.$(OBJ),$(OBJS)) 27 | 28 | .PHONY: all clean tests uninstall install rebuild 29 | 30 | %.$(OBJ):%.$(C) 31 | mkdir -p bin 32 | @echo Compiling $(basename $<)... 33 | $(CC) -c $(CFLAGS) $< $(OBJFLAG)$@ 34 | 35 | all: pre-build $(BIN) $(TESTS_BIN) 36 | 37 | $(BIN): $(OBJS) 38 | @echo Linking... 39 | $(CC) $(LDFLAGS) $^ $(LIBS) $(BINFLAG) $(BIN) 40 | 41 | $(TESTS_BIN): $(TESTS_OBJS) 42 | @echo Linking... 43 | $(CC) $(LDFLAGS) $^ $(LIBS) $(BINFLAG) $(TESTS_BIN) 44 | 45 | pre-build: 46 | ./configurePath.sh 47 | 48 | clean: 49 | rm -rf $(SOURCE_PATH)*.$(OBJ) $(BIN) 50 | rm -rf $(TESTS_PATH)*.$(OBJ) $(TESTS_BIN) 51 | 52 | tests: all 53 | ./bin/mbpfan-tests 54 | 55 | uninstall: 56 | rm /usr/sbin/mbpfan 57 | rm /etc/mbpfan.conf 58 | rm /lib/systemd/system/mbpfan.service 59 | rm /usr/share/man/man8/mbpfan.8.gz 60 | rm -rf /usr/share/doc/mbpfan 61 | 62 | install: all 63 | install -d $(DESTDIR)/usr/sbin 64 | install -d $(DESTDIR)/etc 65 | install -d $(DESTDIR)/lib/systemd/system 66 | install -d $(DESTDIR)/usr/share/doc/mbpfan 67 | install $(BIN) $(DESTDIR)/usr/sbin 68 | install -m644 $(CONF) $(DESTDIR)/etc 69 | install -m644 $(DOC) $(DESTDIR)/usr/share/doc/mbpfan 70 | install -d $(DESTDIR)/usr/share/man/man8 71 | install -m644 $(MAN) $(DESTDIR)/usr/share/man/man8 72 | @echo "" 73 | @echo "******************" 74 | @echo "INSTALL COMPLETED" 75 | @echo "******************" 76 | @echo "" 77 | @echo "A configuration file has been copied (might overwrite existing file) to /etc/mbpfan.conf." 78 | @echo "See README.md file to have mbpfan automatically started at system boot." 79 | @echo "" 80 | @echo "Please run the tests now with the command" 81 | @echo " sudo make tests" 82 | @echo "" 83 | rebuild: clean all 84 | #rebuild is not entirely correct 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mbpfan 2 | 3 | This is a fork of [linux-on-mac/mbpfan](https://github.com/linux-on-mac/mbpfan). For full documentation please read the readme there. 4 | 5 | ## T2 Macs 6 | 7 | This repo provides an extra script to configure this tool to work with Macs that have a t2 patched kernel. 8 | 9 | Tested Models: 10 | 11 | - MacBook Pro 16,1 (Ubuntu, 5.7.19-mbp) 12 | - MacBook Pro 15,1 (Ubuntu, 5.7.15-mbp-alt) 13 | - MacBook Pro 15,1 (Arch, 5.7.19-mbp) 14 | - MacBook Pro 15,1 (Arch, 5.10.12-mbp) 15 | 16 | ## Install 17 | 18 | See [wiki.t2linux.org/guides/fan](https://wiki.t2linux.org/guides/fan/) 19 | 20 | ## Usage 21 | 22 | Usage: ./mbpfan OPTION(S) 23 | 24 | -h Show the help screen 25 | -f Run in foreground 26 | -v Be (a lot) verbose 27 | 28 | ## License 29 | 30 | GNU General Public License version 3 31 | 32 | ## Credits 33 | 34 | **This Project Is Based On:** 35 | 36 | - [github.com/linux-on-mac/mbpfan](https://github.com/linux-on-mac/mbpfan) 37 | - [allanmcrae.com/2010/05/simple-macbook-pro-fan-daemon](http://allanmcrae.com/2010/05/simple-macbook-pro-fan-daemon/) 38 | - [allanmcrae.com/2011/08/mbp-fan-daemon-update](http://allanmcrae.com/2011/08/mbp-fan-daemon-update/) 39 | - [launchpad.net/macfanctld](https://launchpad.net/macfanctld) 40 | - [lobotomo.com/products/FanControl](https://www.lobotomo.com/products/FanControl/) 41 | - Work by [t2linux.org](https://github.com/t2linux) 42 | 43 | **This Project uses following library:** 44 | 45 | - [ANSI C Application Settings Management](http://pokristensson.com/settings.html) by Per Ola Kristensson. 46 | -------------------------------------------------------------------------------- /configurePath.sh: -------------------------------------------------------------------------------- 1 | found=$(find /sys/devices -wholename "*APP0001:00/fan*" | head -1) 2 | path=$(dirname $found) 3 | 4 | echo " ------------------------------------- " 5 | echo " Found fan at '${path}'" 6 | echo "" 7 | echo " For me the output was something like '/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:102/APP0001:00'." 8 | echo "" 9 | echo " If your path looks completely different and the tests fail, " 10 | echo " try just using the normal (unpatched) version (git reset --hard origin/master)." 11 | echo " If that doesn't work either feel free to open an issue on github" 12 | echo "" 13 | 14 | line1="#define APPLESMC_PATH \"$path\"" 15 | line2=" const char *path_begin = \"$path/fan\";" 16 | 17 | sed -i "54s|.*|$line1|" "./src/mbpfan.c" 18 | sed -i "277s|.*|$line2|" "./src/mbpfan.c" 19 | 20 | echo " Patched mbpfan.c" 21 | echo "" 22 | echo " See further install instructions here: " 23 | echo " https://wiki.t2linux.org/guides/fan/" 24 | echo " It's advised to also run the tests to check if the patch was successful" 25 | echo " ------------------------------------- " 26 | 27 | 28 | -------------------------------------------------------------------------------- /mbpfan.8.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networkException/mbpfan/32887b4108b7886b5d4944a2909fcef30d4bb40c/mbpfan.8.gz -------------------------------------------------------------------------------- /mbpfan.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | # see https://ineed.coffee/3838/a-beginners-tutorial-for-mbpfan-under-ubuntu for the values 3 | # 4 | # mbpfan will load the max / min speed of from the files produced by the applesmc driver. If these files are not found it will set all fans to the default of min_speed = 2000 and max_speed = 6200 5 | # by setting the values for the speeds in this config it will override whatever it finds in: 6 | # /sys/devices/platform/applesmc.768/fan*_min 7 | # /sys/devices/platform/applesmc.768/fan*_max 8 | # or the defaults. 9 | # 10 | # multiple fans can be configured by using the config key of min_fan*_speed and max_fan*_speed 11 | # the number used will correlate to the file number of the fan in the applesmc driver that are used to control the fan speed. 12 | # 13 | #min_fan1_speed = 2000 # put the *lowest* value of "cat /sys/devices/platform/applesmc.768/fan*_min" 14 | #max_fan1_speed = 6200 # put the *highest* value of "cat /sys/devices/platform/applesmc.768/fan*_max" 15 | low_temp = 63 # try ranges 55-63, default is 63 16 | high_temp = 66 # try ranges 58-66, default is 66 17 | max_temp = 86 # take highest number returned by "cat /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_max", divide by 1000 18 | polling_interval = 1 # default is 1 seconds 19 | -------------------------------------------------------------------------------- /mbpfan.conf.test0: -------------------------------------------------------------------------------- 1 | [general] 2 | # see https://ineed.coffee/3838/a-beginners-tutorial-for-mbpfan-under-ubuntu for the values 3 | min_fan1_speed = 2000 # put the *lowest* value of "cat /sys/devices/platform/applesmc.768/fan*_min" 4 | max_fan1_speed = 6200 # put the *highest* value of "cat /sys/devices/platform/applesmc.768/fan*_max" 5 | low_temp = 63 # try ranges 55-63, default is 63 6 | high_temp = 66 # try ranges 58-66, default is 66 7 | max_temp = 86 # take highest number returned by "cat /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_max", divide by 1000 8 | polling_interval = 7 # default is 1 second 9 | -------------------------------------------------------------------------------- /mbpfan.conf.test1: -------------------------------------------------------------------------------- 1 | [general] 2 | min_fan1_speed = 2000 # default is 2000 3 | max_fan1_speed = 2600 # default is 2600 4 | low_temp = 63 # try ranges 55-63, default is 63 5 | high_temp = 66 # try ranges 58-66, default is 66 6 | max_temp = 86 # do not set it > 90, default is 86 7 | polling_interval = 2 # default is 1 second 8 | -------------------------------------------------------------------------------- /mbpfan.conf.test2: -------------------------------------------------------------------------------- 1 | [general] 2 | min_fan1_speed = 2000 # default is 2000 3 | min_fan2_speed = 2000 # default is 6200 4 | low_temp = 63 # try ranges 55-63, default is 63 5 | high_temp = 66 # try ranges 58-66, default is 66 6 | max_temp = 86 # do not set it > 90, default is 86 7 | polling_interval = 1 # default is 1 second 8 | -------------------------------------------------------------------------------- /mbpfan.init.debian: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: mbpfan 4 | # Required-Start: $remote_fs $syslog 5 | # Required-Stop: $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: mbpfan initscript 9 | # Description: Start the mbpfan daemon 10 | ### END INIT INFO 11 | 12 | DESC="Start the mbpfan daemon" 13 | NAME=mbpfan 14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 15 | DAEMON=/usr/sbin/$NAME 16 | PIDFILE=/var/run/$NAME.pid 17 | SCRIPTNAME=/etc/init.d/$NAME 18 | DAEMON_ARGS="" 19 | 20 | # Exit if the package is not installed 21 | [ -x "$DAEMON" ] || exit 0 22 | 23 | # Load the VERBOSE setting and other rcS variables 24 | . /lib/init/vars.sh 25 | 26 | # Define LSB log_* functions. 27 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. 28 | . /lib/lsb/init-functions 29 | 30 | # Function that starts the daemon/service 31 | do_start() 32 | { 33 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ 34 | || return 1 35 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ 36 | $DAEMON_ARGS \ 37 | || return 2 38 | } 39 | 40 | # Function that stops the daemon/service 41 | do_stop() 42 | { 43 | # Return 44 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME 45 | RETVAL="$?" 46 | [ "$RETVAL" = 2 ] && return 2 47 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON 48 | [ "$?" = 2 ] && return 2 49 | return "$RETVAL" 50 | } 51 | 52 | # Function that sends a SIGHUP to the daemon/service 53 | do_reload() 54 | { 55 | start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME 56 | return 0 57 | } 58 | 59 | case "$1" in 60 | start) 61 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" 62 | do_start 63 | case "$?" in 64 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 65 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 66 | esac 67 | ;; 68 | stop) 69 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 70 | do_stop 71 | case "$?" in 72 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 73 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 74 | esac 75 | ;; 76 | status) 77 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? 78 | ;; 79 | restart|force-reload) 80 | log_daemon_msg "Restarting $DESC" "$NAME" 81 | do_stop 82 | case "$?" in 83 | 0|1) 84 | do_start 85 | case "$?" in 86 | 0) log_end_msg 0 ;; 87 | 1) log_end_msg 1 ;; # Old process is still running 88 | *) log_end_msg 1 ;; # Failed to start 89 | esac 90 | ;; 91 | *) 92 | # Failed to stop 93 | log_end_msg 1 94 | ;; 95 | esac 96 | ;; 97 | *) 98 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 99 | exit 3 100 | ;; 101 | esac 102 | 103 | : 104 | -------------------------------------------------------------------------------- /mbpfan.init.gentoo: -------------------------------------------------------------------------------- 1 | #!/sbin/openrc-run 2 | 3 | depend() { 4 | use logger 5 | } 6 | 7 | start() { 8 | ebegin "Starting mbpfan" 9 | start-stop-daemon --start --quiet --exec /usr/sbin/mbpfan 10 | eend $? 11 | } 12 | 13 | stop() { 14 | ebegin "Stopping mbpfan" 15 | start-stop-daemon --stop --quiet --exec /usr/sbin/mbpfan 16 | eend $? 17 | } 18 | 19 | -------------------------------------------------------------------------------- /mbpfan.init.redhat: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mbpfan Start the mbpfan daemon 4 | # 5 | # chkconfig: 2 3 4 5 15 85 6 | # description: Start the mbpfan daemon 7 | 8 | ### BEGIN INIT INFO 9 | # Provides: mbpfan 10 | # Required-Start: $remote_fs $syslog 11 | # Required-Stop: $remote_fs $syslog 12 | # Default-Start: 2 3 4 5 13 | # Default-Stop: 0 1 6 14 | # Short-Description: mbpfan initscript 15 | # Description: Start the mbpfan daemon 16 | ### END INIT INFO 17 | 18 | # Source function library. 19 | . /etc/rc.d/init.d/functions 20 | 21 | exec="/usr/sbin/mbpfan" 22 | prog="mbpfan" 23 | 24 | [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog 25 | 26 | lockfile=/var/lock/subsys/$prog 27 | 28 | start() { 29 | [ -x $exec ] || exit 5 30 | echo -n $"Starting $prog: " 31 | daemon $exec 32 | retval=$? 33 | echo 34 | [ $retval -eq 0 ] && touch $lockfile 35 | return $retval 36 | } 37 | 38 | stop() { 39 | echo -n $"Stopping $prog: " 40 | killproc $prog 41 | retval=$? 42 | echo 43 | [ $retval -eq 0 ] && rm -f $lockfile 44 | return $retval 45 | } 46 | 47 | restart() { 48 | stop 49 | start 50 | } 51 | 52 | reload() { 53 | restart 54 | } 55 | 56 | force_reload() { 57 | restart 58 | } 59 | 60 | rh_status() { 61 | # run checks to determine if the service is running or use generic status 62 | status $prog 63 | } 64 | 65 | rh_status_q() { 66 | rh_status >/dev/null 2>&1 67 | } 68 | 69 | 70 | case "$1" in 71 | start) 72 | rh_status_q && exit 0 73 | $1 74 | ;; 75 | stop) 76 | rh_status_q || exit 0 77 | $1 78 | ;; 79 | restart) 80 | $1 81 | ;; 82 | reload) 83 | rh_status_q || exit 7 84 | $1 85 | ;; 86 | force-reload) 87 | force_reload 88 | ;; 89 | status) 90 | rh_status 91 | ;; 92 | condrestart|try-restart) 93 | rh_status_q || exit 0 94 | restart 95 | ;; 96 | *) 97 | echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" 98 | exit 2 99 | esac 100 | exit $? 101 | 102 | -------------------------------------------------------------------------------- /mbpfan.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=A fan manager daemon for MacBook Pro 3 | After=syslog.target 4 | After=sysinit.target 5 | 6 | [Service] 7 | Type=simple 8 | ExecStart=/usr/sbin/mbpfan -f 9 | ExecReload=/usr/bin/kill -HUP $MAINPID 10 | PIDFile=/var/run/mbpfan.pid 11 | Restart=always 12 | 13 | [Install] 14 | WantedBy=sysinit.target 15 | -------------------------------------------------------------------------------- /mbpfan.spec: -------------------------------------------------------------------------------- 1 | Name: mbpfan 2 | URL: https://github.com/dgraziotin/mbpfan 3 | License: GPLv3 4 | Group: System Environment/Daemons 5 | Version: %{SOURCE_VERSION} 6 | Release: 3 7 | Summary: A simple daemon to control fan speed on all MacBook/MacBook Pros (probably all Apple computers) for Linux 3.x.x and 4.x.x 8 | Source: v%{version}.tar.gz 9 | 10 | %description 11 | This is an enhanced version of Allan McRae mbpfan 12 | 13 | mbpfan is a daemon that uses input from coretemp module and sets the fan speed using the applesmc module. This enhanced version assumes any number of processors and fans (max. 10). 14 | 15 | It only uses the temperatures from the processors as input. 16 | It requires coretemp and applesmc kernel modules to be loaded. 17 | It requires root use 18 | It daemonizes or stays in foreground 19 | Verbose mode for both syslog and stdout 20 | Users can configure it using the file /etc/mbpfan.conf 21 | 22 | %prep 23 | %setup -q -n %{name}-%{version} 24 | 25 | %build 26 | make 27 | 28 | %install 29 | install -D -m755 bin/mbpfan $RPM_BUILD_ROOT/usr/sbin/mbpfan 30 | install -D -m644 mbpfan.conf $RPM_BUILD_ROOT/etc/mbpfan.conf 31 | install -D -m644 mbpfan.service $RPM_BUILD_ROOT/usr/lib/systemd/system/mbpfan.service 32 | 33 | %clean 34 | rm -rf $RPM_BUILD_ROOT 35 | 36 | %post 37 | %systemd_post mbpfan.service 38 | echo "mbpfan will auto detect sane values for min and max fan speeds." 39 | echo "If you want to customize these values please edit:" 40 | echo "/etc/mbpfan.conf" 41 | echo "To start the daemon now type:" 42 | echo "systemctl start mbpfan" 43 | echo "To run also at boot, type:" 44 | echo "systemctl enable mbpfan" 45 | 46 | %preun 47 | %systemd_preun mbpfan.service 48 | 49 | %postun 50 | %systemd_postun_with_restart mbpfan.service 51 | 52 | %files 53 | %defattr (-,root,root) 54 | %doc AUTHORS README.md 55 | /usr/sbin/mbpfan 56 | %config /etc/mbpfan.conf 57 | /usr/lib/systemd/system/mbpfan.service 58 | 59 | %changelog 60 | * Mon Sep 10 2018 Michele Codutti - 2.0.2-3 61 | - Removed autoconfig with suggested procedure because has been integrated on mbpfan. 62 | 63 | * Sun Aug 19 2018 Michele Codutti - 2.0.2-2 64 | - Autoconfig with suggested procedure. 65 | - Initial packaging 66 | -------------------------------------------------------------------------------- /mbpfan.upstart: -------------------------------------------------------------------------------- 1 | # mbpfan - A simple daemon to control fan speed on all Macbook/Macbook Pros \ 2 | # (probably all Apple computers) for Linux 3.x.x 3 | 4 | description "mbpfan" 5 | 6 | start on filesystem or runlevel [2345] 7 | stop on runlevel [!2345] 8 | 9 | respawn 10 | umask 022 11 | 12 | console log 13 | 14 | exec /usr/sbin/mbpfan -f 15 | -------------------------------------------------------------------------------- /src/daemon.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Peter Lombardo 3 | * Modifications (2012) by Ismail Khatib 4 | * Modifications (2012-present) by Daniel Graziotin 5 | * Modifications (2017-present) by Robert Musial 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | */ 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "mbpfan.h" 34 | #include "global.h" 35 | #include "daemon.h" 36 | #include "util.h" 37 | 38 | int daemonize = 1; 39 | int verbose = 0; 40 | 41 | int write_pid(int pid) 42 | { 43 | FILE *file = NULL; 44 | file = fopen(PROGRAM_PID, "w"); 45 | 46 | if(file != NULL) { 47 | fprintf(file, "%d", pid); 48 | fclose(file); 49 | return 1; 50 | 51 | } else { 52 | return 0; 53 | } 54 | } 55 | 56 | int read_pid() 57 | { 58 | FILE *file = NULL; 59 | int pid = -1; 60 | file = fopen(PROGRAM_PID, "r"); 61 | 62 | if(file != NULL) { 63 | fscanf(file, "%d", &pid); 64 | fclose(file); 65 | if (kill(pid, 0) == -1 && errno == ESRCH) 66 | { /* a process with such a pid does not exist, remove the pid file */ 67 | if (remove(PROGRAM_PID) == 0) { 68 | return -1; 69 | } 70 | } 71 | return pid; 72 | } 73 | 74 | return -1; 75 | } 76 | 77 | int delete_pid() 78 | { 79 | return remove(PROGRAM_PID); 80 | } 81 | 82 | static void cleanup_and_exit(int exit_code) 83 | { 84 | delete_pid(); 85 | set_fans_auto(fans); 86 | 87 | struct s_fans *next_fan; 88 | while (fans != NULL) { 89 | next_fan = fans->next; 90 | if (fans->file != NULL) { 91 | fclose(fans->file); 92 | } 93 | free(fans->label); 94 | free(fans->fan_output_path); 95 | free(fans->fan_manual_path); 96 | free(fans); 97 | fans = next_fan; 98 | } 99 | 100 | struct s_sensors *next_sensor; 101 | while (sensors != NULL) { 102 | next_sensor = sensors->next; 103 | if (sensors->file != NULL) { 104 | fclose(sensors->file); 105 | } 106 | free(sensors->path); 107 | free(sensors); 108 | sensors = next_sensor; 109 | } 110 | 111 | exit(exit_code); 112 | } 113 | 114 | void signal_handler(int signal) 115 | { 116 | 117 | switch(signal) { 118 | case SIGHUP: 119 | syslog(LOG_WARNING, "Received SIGHUP signal."); 120 | retrieve_settings(NULL, fans); 121 | break; 122 | 123 | case SIGTERM: 124 | syslog(LOG_WARNING, "Received SIGTERM signal."); 125 | cleanup_and_exit(EXIT_SUCCESS); 126 | break; 127 | 128 | case SIGQUIT: 129 | syslog(LOG_WARNING, "Received SIGQUIT signal."); 130 | cleanup_and_exit(EXIT_SUCCESS); 131 | break; 132 | 133 | case SIGINT: 134 | syslog(LOG_WARNING, "Received SIGINT signal."); 135 | cleanup_and_exit(EXIT_SUCCESS); 136 | break; 137 | 138 | default: 139 | syslog(LOG_WARNING, "Unhandled signal (%d) %s", signal, strsignal(signal)); 140 | break; 141 | } 142 | } 143 | 144 | void go_daemon(void (*fan_control)()) 145 | { 146 | 147 | // Setup signal handling before we start 148 | signal(SIGHUP, signal_handler); 149 | signal(SIGTERM, signal_handler); 150 | signal(SIGQUIT, signal_handler); 151 | signal(SIGINT, signal_handler); 152 | 153 | // Setup syslog logging - see SETLOGMASK(3) 154 | if(verbose) { 155 | setlogmask(LOG_UPTO(LOG_DEBUG)); 156 | openlog(PROGRAM_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER); 157 | 158 | } else { 159 | setlogmask(LOG_UPTO(LOG_INFO)); 160 | openlog(PROGRAM_NAME, LOG_CONS, LOG_USER); 161 | } 162 | 163 | mbp_log(LOG_INFO, "%s %s starting up", PROGRAM_NAME, PROGRAM_VERSION); 164 | 165 | // configure timer slack 166 | int err = prctl(PR_SET_TIMERSLACK, 1000 * 1000 * 1000, 0, 0, 0); 167 | if (err == -1) { 168 | perror("prctl"); 169 | } 170 | 171 | pid_t pid_slave; 172 | pid_t sid_slave; 173 | 174 | if (daemonize) { 175 | 176 | pid_slave = fork(); 177 | 178 | if (pid_slave < 0) { 179 | exit(EXIT_FAILURE); 180 | } 181 | 182 | if (pid_slave > 0) { 183 | signal(SIGCHLD, SIG_IGN); 184 | // kill the father 185 | exit(EXIT_SUCCESS); 186 | } 187 | 188 | umask(0022); 189 | 190 | // new sid_slave for the child process 191 | sid_slave = setsid(); 192 | 193 | if (sid_slave < 0) { 194 | exit(EXIT_FAILURE); 195 | } 196 | 197 | if ((chdir("/")) < 0) { 198 | exit(EXIT_FAILURE); 199 | } 200 | 201 | 202 | 203 | /* Close out the standard file descriptors */ 204 | close(STDIN_FILENO); 205 | close(STDOUT_FILENO); 206 | close(STDERR_FILENO); 207 | } 208 | 209 | 210 | int current_pid = getpid(); 211 | 212 | if (read_pid() == -1) { 213 | if (verbose) { 214 | mbp_log(LOG_INFO, "Writing a new .pid file with value %d at: %s", current_pid, PROGRAM_PID); 215 | } 216 | 217 | if (write_pid(current_pid) == 0) { 218 | mbp_log(LOG_ERR, "Can not create a .pid file at: %s. Aborting", PROGRAM_PID); 219 | exit(EXIT_FAILURE); 220 | 221 | } else { 222 | if (verbose) { 223 | mbp_log(LOG_INFO, "Successfully written a new .pid file with value %d at: %s", current_pid, PROGRAM_PID); 224 | } 225 | } 226 | 227 | } else { 228 | mbp_log(LOG_ERR, "A previously created .pid file exists at: %s. Aborting", PROGRAM_PID); 229 | exit(EXIT_FAILURE); 230 | } 231 | 232 | 233 | fan_control(); 234 | 235 | if(daemonize) { 236 | syslog(LOG_INFO, "%s daemon exiting", PROGRAM_NAME); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/daemon.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) (2012-present) Daniel Graziotin 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | */ 15 | 16 | #ifndef _DAEMON_H_ 17 | #define _DAEMON_H_ 18 | 19 | /** 20 | * Write the PID of the forked daemon to the 21 | * .pid file defined in char *program_pid 22 | * Return TRUE on success 23 | * Return FALSE otherwise 24 | */ 25 | int write_pid(int pid); 26 | 27 | /** 28 | * Read PID of the forked daemon from the 29 | * .pid file defined in char *program_pid 30 | * Return the pid on success 31 | * Return -1 otherwise 32 | */ 33 | int read_pid(); 34 | 35 | /** 36 | * Deletes the .pid file defined in 37 | * char *program_pid 38 | * Return TRUE on success 39 | * Return FALSE otherwise 40 | */ 41 | int delete_pid(); 42 | 43 | /** 44 | * ...handles signals :-) 45 | */ 46 | void signal_handler(int signal); 47 | 48 | /** 49 | * Daemonizes 50 | */ 51 | void go_daemon(void (*function)()); 52 | 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/global.h: -------------------------------------------------------------------------------- 1 | #ifndef _GLOBAL_H_ 2 | #define _GLOBAL_H_ 3 | 4 | #define PROGRAM_NAME "mbpfan" 5 | #define PROGRAM_VERSION "2.2.0" 6 | #define PROGRAM_PID "/var/run/mbpfan.pid" 7 | 8 | extern int daemonize; 9 | extern int verbose; 10 | 11 | struct s_sensors { 12 | FILE* file; 13 | char* path; 14 | unsigned int temperature; 15 | struct s_sensors *next; 16 | }; 17 | 18 | struct s_fans { 19 | FILE* file; 20 | char* path; // TODO: unused 21 | char* label; 22 | char* fan_output_path; 23 | char* fan_manual_path; 24 | int step_up; 25 | int step_down; 26 | int fan_id; 27 | int old_speed; 28 | int fan_max_speed; 29 | int fan_min_speed; 30 | struct s_fans *next; 31 | }; 32 | 33 | typedef struct s_sensors t_sensors; 34 | typedef struct s_fans t_fans; 35 | 36 | extern t_sensors* sensors; 37 | extern t_fans* fans; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) (2012-present) Daniel Graziotin 3 | * Modifications (2017-present) by Robert Musial 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | */ 16 | 17 | /** 18 | * Code formatted with astyle -A3 -s --break-blocks=all --add-brackets *.c *.h 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "mbpfan.h" 28 | #include "daemon.h" 29 | #include "global.h" 30 | #include "util.h" 31 | 32 | void print_usage(int argc, char *argv[]) 33 | { 34 | if (argc >=1) { 35 | printf("Usage: %s OPTION(S) \n", argv[0]); 36 | printf("Options:\n"); 37 | printf("\t-h Show this help screen\n"); 38 | printf("\t-f Run in foreground\n"); 39 | printf("\t-v Be (a lot) verbose\n"); 40 | printf("\n"); 41 | } 42 | } 43 | 44 | int main(int argc, char *argv[]) 45 | { 46 | 47 | int c; 48 | 49 | while( (c = getopt(argc, argv, "hfv|help")) != -1) { 50 | switch(c) { 51 | case 'h': 52 | print_usage(argc, argv); 53 | exit(EXIT_SUCCESS); 54 | break; 55 | 56 | case 'f': 57 | daemonize = 0; 58 | break; 59 | 60 | case 'v': 61 | verbose = 1; 62 | break; 63 | 64 | default: 65 | print_usage(argc, argv); 66 | exit(EXIT_SUCCESS); 67 | break; 68 | } 69 | } 70 | 71 | check_requirements(argv[0]); 72 | 73 | // pointer to mbpfan() function in mbpfan.c 74 | void (*fan_control)() = mbpfan; 75 | go_daemon(fan_control); 76 | exit(EXIT_SUCCESS); 77 | } 78 | -------------------------------------------------------------------------------- /src/mbpfan.c: -------------------------------------------------------------------------------- 1 | /** 2 | * mbpfan.c - automatically control fan for MacBook Pro 3 | * Copyright (C) 2010 Allan McRae 4 | * Modifications by Rafael Vega 5 | * Modifications (2012) by Ismail Khatib 6 | * Modifications (2012-present) by Daniel Graziotin [CURRENT MAINTAINER] 7 | * Modifications (2017-present) by Robert Musial 8 | * Modifications (2018-present) by Ati Sharma 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * 21 | * Notes: 22 | * Assumes any number of processors, cores, sensors and fans 23 | * (as defined in NUM_PROCESSORS, NUM_HWMONS, NUM_TEMP_INPUTS and NUM_FANS) 24 | * It uses only the temperatures from the processors as input. 25 | * Requires coretemp and applesmc kernel modules to be loaded. 26 | * Requires root use 27 | * 28 | * Tested models: see README.md 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include "mbpfan.h" 45 | #include "global.h" 46 | #include "settings.h" 47 | #include "util.h" 48 | 49 | /* lazy min/max... */ 50 | #define min(a,b) ((a) < (b) ? (a) : (b)) 51 | #define max(a,b) ((a) > (b) ? (a) : (b)) 52 | 53 | #define CORETEMP_PATH "/sys/devices/platform/coretemp.0" 54 | #define APPLESMC_PATH "/sys/devices/platform/applesmc.768" 55 | 56 | /* temperature thresholds 57 | * low_temp - temperature below which fan speed will be at minimum 58 | * high_temp - fan will increase speed when higher than this temperature 59 | * max_temp - fan will run at full speed above this temperature */ 60 | int low_temp = 63; // try ranges 55-63 61 | int high_temp = 66; // try ranges 58-66 62 | int max_temp = 86; // do not set it > 90 63 | 64 | // maximum number of processors etc supported 65 | #define NUM_PROCESSORS 6 66 | #define NUM_HWMONS 12 67 | #define NUM_TEMP_INPUTS 64 68 | #define NUM_FANS 10 69 | // sane defaults when user provides unexpected values 70 | #define MIN_FAN_SPEED_DEFAULT 500 71 | #define MAX_FAN_SPEED_DEFAULT 6500 72 | 73 | int polling_interval = 1; 74 | 75 | t_sensors* sensors = NULL; 76 | t_fans* fans = NULL; 77 | 78 | char *smprintf(const char *fmt, ...) 79 | { 80 | char *buf; 81 | int cnt; 82 | va_list ap; 83 | 84 | // find buffer length 85 | va_start(ap, fmt); 86 | cnt = vsnprintf(NULL, 0, fmt, ap); 87 | va_end(ap); 88 | if (cnt < 0) { 89 | return NULL; 90 | } 91 | 92 | // create and write to buffer 93 | buf = malloc(cnt + 1); 94 | va_start(ap, fmt); 95 | vsnprintf(buf, cnt + 1, fmt, ap); 96 | va_end(ap); 97 | return buf; 98 | } 99 | 100 | bool is_modern_sensors_path() 101 | { 102 | struct utsname kernel; 103 | uname(&kernel); 104 | 105 | char *str_kernel_version; 106 | str_kernel_version = strtok(kernel.release, "."); 107 | 108 | if (atoi(str_kernel_version) < 3){ 109 | mbp_log(LOG_ERR, "mbpfan detected a pre-3.x.x linux kernel. Detected version: %s. Exiting.\n", kernel.release); 110 | exit(EXIT_FAILURE); 111 | } 112 | 113 | int counter; 114 | 115 | for (counter = 0; counter < NUM_HWMONS; counter++) { 116 | int temp; 117 | for (temp = 1; temp < NUM_TEMP_INPUTS; ++temp) { 118 | char *path = smprintf("/sys/devices/platform/coretemp.0/hwmon/hwmon%d/temp%d_input", counter, temp); 119 | int res = access(path, R_OK); 120 | free(path); 121 | if (res == 0) { 122 | return 1; 123 | } 124 | } 125 | } 126 | 127 | return 0; 128 | } 129 | 130 | 131 | t_sensors *retrieve_sensors() 132 | { 133 | t_sensors *sensors_head = NULL; 134 | t_sensors *s = NULL; 135 | 136 | char *path = NULL; 137 | char *path_begin = NULL; 138 | 139 | const char *path_end = "_input"; 140 | int sensors_found = 0; 141 | 142 | if (!is_modern_sensors_path()) { 143 | if(verbose) { 144 | mbp_log(LOG_INFO, "Using legacy path for kernel < 3.15.0"); 145 | } 146 | 147 | path_begin = strdup("/sys/devices/platform/coretemp.0/temp"); 148 | 149 | } else { 150 | 151 | if(verbose) { 152 | mbp_log(LOG_INFO, "Using new sensor path for kernel >= 3.15.0 or some CentOS versions with kernel 3.10.0 "); 153 | } 154 | 155 | // loop over up to 6 processors 156 | int processor; 157 | for (processor = 0; processor < NUM_PROCESSORS; processor++) { 158 | 159 | if (path_begin != NULL) { 160 | free(path_begin); 161 | } 162 | path_begin = smprintf("/sys/devices/platform/coretemp.%d/hwmon/hwmon", processor); 163 | 164 | int counter; 165 | for (counter = 0; counter < NUM_HWMONS; counter++) { 166 | 167 | char *hwmon_path = smprintf("%s%d", path_begin, counter); 168 | 169 | int res = access(hwmon_path, R_OK); 170 | if (res == 0) { 171 | 172 | free(path_begin); 173 | path_begin = smprintf("%s/temp", hwmon_path); 174 | 175 | if(verbose) { 176 | mbp_log(LOG_INFO, "Found hwmon path at %s", path_begin); 177 | } 178 | 179 | free(hwmon_path); 180 | break; 181 | } 182 | 183 | free(hwmon_path); 184 | } 185 | 186 | int core = 0; 187 | for(core = 0; corepath = strdup(path); 195 | fscanf(file, "%d", &s->temperature); 196 | 197 | if (sensors_head == NULL) { 198 | sensors_head = s; 199 | sensors_head->next = NULL; 200 | 201 | } else { 202 | t_sensors *tmp = sensors_head; 203 | 204 | while (tmp->next != NULL) { 205 | tmp = tmp->next; 206 | } 207 | 208 | tmp->next = s; 209 | tmp->next->next = NULL; 210 | } 211 | 212 | s->file = file; 213 | sensors_found++; 214 | } 215 | 216 | free(path); 217 | path = NULL; 218 | } 219 | } 220 | } 221 | 222 | if(verbose) { 223 | mbp_log(LOG_INFO, "Found %d sensors", sensors_found); 224 | } 225 | 226 | if (sensors_found == 0){ 227 | mbp_log(LOG_CRIT, "mbpfan could not detect any temp sensor. Please contact the developer."); 228 | exit(EXIT_FAILURE); 229 | } 230 | 231 | free(path_begin); 232 | path_begin = NULL; 233 | 234 | return sensors_head; 235 | } 236 | 237 | static int read_value(const char *path) 238 | { 239 | int value = -1; 240 | FILE *file = fopen(path, "r"); 241 | if (file != NULL) { 242 | fscanf(file, "%d", &value); 243 | fclose(file); 244 | } 245 | return value; 246 | } 247 | 248 | static void read_value_str(const char *path, char *str, size_t len) 249 | { 250 | FILE *file = fopen(path, "r"); 251 | if (file != NULL) { 252 | fgets(str, len, file); 253 | fclose(file); 254 | } 255 | } 256 | 257 | static void trim_trailing_whitespace(char *str) 258 | { 259 | for (ssize_t i = strlen(str) - 1; i >= 0; --i) { 260 | if (isspace(str[i]) || str[i] == '\n') { 261 | str[i] = '\0'; 262 | } 263 | } 264 | } 265 | 266 | t_fans *retrieve_fans() 267 | { 268 | t_fans *fans_head = NULL; 269 | t_fans *fan = NULL; 270 | 271 | char *path_output = NULL; 272 | char *path_label = NULL; 273 | char *path_manual = NULL; 274 | char *path_fan_max = NULL; 275 | char *path_fan_min = NULL; 276 | 277 | const char *path_begin = "/sys/devices/platform/applesmc.768/fan"; 278 | const char *path_output_end = "_output"; 279 | const char *path_label_end = "_label"; 280 | const char *path_man_end = "_manual"; 281 | const char *path_max_speed = "_max"; 282 | const char *path_min_speed = "_min"; 283 | 284 | int counter = 0; 285 | int fans_found = 0; 286 | 287 | for(counter = 0; counterfan_output_path = strdup(path_output); 300 | fan->fan_manual_path = strdup(path_manual); 301 | fan->fan_id = counter; 302 | 303 | int fan_speed = read_value(path_fan_min); 304 | if(fan_speed == -1 || fan_speed < MIN_FAN_SPEED_DEFAULT) 305 | fan->fan_min_speed = MIN_FAN_SPEED_DEFAULT; 306 | else 307 | fan->fan_min_speed = fan_speed; 308 | 309 | fan_speed = read_value(path_fan_max); 310 | if(fan_speed == -1 || fan_speed > MAX_FAN_SPEED_DEFAULT) 311 | fan->fan_max_speed = MAX_FAN_SPEED_DEFAULT; 312 | else 313 | fan->fan_max_speed = fan_speed; 314 | 315 | size_t max_label_len = 64; 316 | fan->label = malloc(max_label_len); 317 | read_value_str(path_label, fan->label, max_label_len); 318 | trim_trailing_whitespace(fan->label); 319 | 320 | fan->old_speed = 0; 321 | 322 | if (fans_head == NULL) { 323 | fans_head = fan; 324 | fans_head->next = NULL; 325 | 326 | } else { 327 | t_fans *tmp = fans_head; 328 | 329 | while (tmp->next != NULL) { 330 | tmp = tmp->next; 331 | } 332 | 333 | tmp->next = fan; 334 | tmp->next->next = NULL; 335 | } 336 | 337 | fan->file = file; 338 | fans_found++; 339 | } 340 | free(path_fan_min); 341 | path_fan_min = NULL; 342 | free(path_label); 343 | path_label = NULL; 344 | free(path_fan_max); 345 | path_fan_max = NULL; 346 | free(path_output); 347 | path_output = NULL; 348 | free(path_manual); 349 | path_manual = NULL; 350 | } 351 | 352 | if(verbose) { 353 | mbp_log(LOG_INFO, "Found %d fans", fans_found); 354 | } 355 | 356 | if (fans_found == 0){ 357 | mbp_log(LOG_CRIT, "mbpfan could not detect any fan. Please contact the developer."); 358 | exit(EXIT_FAILURE); 359 | } 360 | 361 | return fans_head; 362 | } 363 | 364 | static void set_fans_mode(t_fans *fans, int mode) 365 | { 366 | t_fans *tmp = fans; 367 | FILE *file; 368 | 369 | while(tmp != NULL) { 370 | file = fopen(tmp->fan_manual_path, "rw+"); 371 | 372 | if(file != NULL) { 373 | fprintf(file, "%d", mode); 374 | fclose(file); 375 | } 376 | 377 | tmp = tmp->next; 378 | } 379 | } 380 | 381 | void set_fans_man(t_fans *fans) 382 | { 383 | 384 | set_fans_mode(fans, 1); 385 | } 386 | 387 | void set_fans_auto(t_fans *fans) 388 | { 389 | 390 | set_fans_mode(fans, 0); 391 | } 392 | 393 | t_sensors *refresh_sensors(t_sensors *sensors) 394 | { 395 | t_sensors *tmp = sensors; 396 | 397 | while(tmp != NULL) { 398 | if(tmp->file != NULL) { 399 | char buf[16]; 400 | int len = pread(fileno(tmp->file), buf, sizeof(buf), /*offset=*/ 0); 401 | buf[len] = '\0'; 402 | tmp->temperature = strtod(buf, NULL); 403 | } 404 | 405 | tmp = tmp->next; 406 | } 407 | 408 | return sensors; 409 | } 410 | 411 | /* Controls the speed of a fan */ 412 | void set_fan_speed(t_fans* fan, int speed) 413 | { 414 | if(fan != NULL && fan->file != NULL && fan->old_speed != speed) { 415 | char buf[16]; 416 | int len = snprintf(buf, sizeof(buf), "%d", speed); 417 | int res = pwrite(fileno(fan->file), buf, len, /*offset=*/ 0); 418 | if (res == -1) { 419 | perror("Could not set fan speed"); 420 | } 421 | fan->old_speed = speed; 422 | } 423 | } 424 | 425 | void set_fan_minimum_speed(t_fans* fans) 426 | { 427 | t_fans *tmp = fans; 428 | 429 | while(tmp != NULL) { 430 | set_fan_speed(tmp,tmp->fan_min_speed); 431 | tmp = tmp->next; 432 | } 433 | } 434 | unsigned short get_temp(t_sensors* sensors) 435 | { 436 | sensors = refresh_sensors(sensors); 437 | unsigned int temp = 0; 438 | 439 | t_sensors* tmp = sensors; 440 | 441 | while(tmp != NULL) { 442 | temp = max(temp, tmp->temperature); 443 | tmp = tmp->next; 444 | } 445 | 446 | return temp / 1000; 447 | } 448 | 449 | void retrieve_settings(const char* settings_path, t_fans* fans) 450 | { 451 | Settings *settings = NULL; 452 | int result = 0; 453 | FILE *f = NULL; 454 | 455 | if (settings_path == NULL) { 456 | f = fopen("/etc/mbpfan.conf", "r"); 457 | 458 | } else { 459 | f = fopen(settings_path, "r"); 460 | } 461 | 462 | 463 | if (f == NULL) { 464 | /* Could not open configfile */ 465 | if(verbose) { 466 | mbp_log(LOG_INFO, "Couldn't open configfile, using defaults"); 467 | } 468 | 469 | } else { 470 | settings = settings_open(f); 471 | fclose(f); 472 | 473 | if (settings == NULL) { 474 | /* Could not read configfile */ 475 | if(verbose) { 476 | mbp_log(LOG_WARNING, "Couldn't read configfile"); 477 | } 478 | 479 | } else { 480 | 481 | t_fans *fan = fans; 482 | 483 | while(fan != NULL) { 484 | 485 | char* config_key; 486 | config_key = smprintf("min_fan%d_speed", fan->fan_id); 487 | /* Read configfile values */ 488 | result = settings_get_int(settings, "general", config_key); 489 | if (result != 0) { 490 | fan->fan_min_speed = result; 491 | } 492 | free(config_key); 493 | 494 | config_key = smprintf("max_fan%d_speed", fan->fan_id); 495 | result = settings_get_int(settings, "general", config_key); 496 | 497 | if (result != 0) { 498 | fan->fan_max_speed = result; 499 | } 500 | free(config_key); 501 | fan = fan->next; 502 | } 503 | result = settings_get_int(settings, "general", "low_temp"); 504 | 505 | if (result != 0) { 506 | low_temp = result; 507 | } 508 | 509 | result = settings_get_int(settings, "general", "high_temp"); 510 | 511 | if (result != 0) { 512 | high_temp = result; 513 | } 514 | 515 | result = settings_get_int(settings, "general", "max_temp"); 516 | 517 | if (result != 0) { 518 | max_temp = result; 519 | } 520 | 521 | result = settings_get_int(settings, "general", "polling_interval"); 522 | 523 | if (result != 0) { 524 | polling_interval = result; 525 | } 526 | 527 | /* Destroy the settings object */ 528 | settings_delete(settings); 529 | } 530 | } 531 | } 532 | 533 | void check_requirements(const char* program_path) 534 | { 535 | 536 | /** 537 | * Check for root 538 | */ 539 | 540 | uid_t uid=getuid(), euid=geteuid(); 541 | 542 | if (uid != 0 || euid != 0) { 543 | mbp_log(LOG_ERR, "%s needs root privileges. Please run %s as root. Exiting.", program_path, program_path); 544 | exit(EXIT_FAILURE); 545 | } 546 | 547 | /** 548 | * Check for coretemp and applesmc modules 549 | */ 550 | DIR* dir = opendir(CORETEMP_PATH); 551 | 552 | if (ENOENT == errno) { 553 | mbp_log(LOG_ERR, "%s needs coretemp support. Please either load it or build it into the kernel. Exiting.", program_path); 554 | exit(EXIT_FAILURE); 555 | } 556 | 557 | closedir(dir); 558 | 559 | 560 | dir = opendir(APPLESMC_PATH); 561 | 562 | if (ENOENT == errno) { 563 | mbp_log(LOG_ERR, "%s needs applesmc support. Please either load it or build it into the kernel. Exiting.", program_path); 564 | exit(EXIT_FAILURE); 565 | } 566 | 567 | closedir(dir); 568 | 569 | 570 | } 571 | 572 | void mbpfan() 573 | { 574 | int old_temp, new_temp, fan_speed, steps; 575 | int temp_change; 576 | 577 | sensors = retrieve_sensors(); 578 | fans = retrieve_fans(); 579 | 580 | retrieve_settings(NULL, fans); 581 | 582 | t_fans* fan = fans; 583 | while(fan != NULL) { 584 | 585 | if (fan->fan_min_speed > fan->fan_max_speed) { 586 | mbp_log(LOG_ERR, "Invalid fan speeds: %d %d", fan->fan_min_speed, fan->fan_max_speed); 587 | exit(EXIT_FAILURE); 588 | } 589 | fan = fan->next; 590 | } 591 | 592 | if (low_temp > high_temp || high_temp > max_temp) { 593 | mbp_log(LOG_ERR, "Invalid temperatures: %d %d %d", low_temp, high_temp, max_temp); 594 | exit(EXIT_FAILURE); 595 | } 596 | 597 | set_fans_man(fans); 598 | 599 | new_temp = get_temp(sensors); 600 | set_fan_minimum_speed(fans); 601 | 602 | fan = fans; 603 | while(fan != NULL) { 604 | 605 | fan->step_up = (float)( fan->fan_max_speed - fan->fan_min_speed ) / 606 | (float)( ( max_temp - high_temp ) * ( max_temp - high_temp + 1 ) / 2.0 ); 607 | 608 | fan->step_down = (float)( fan->fan_max_speed - fan->fan_min_speed ) / 609 | (float)( ( max_temp - low_temp ) * ( max_temp - low_temp + 1 ) / 2.0 ); 610 | fan = fan->next; 611 | } 612 | 613 | recalibrate: 614 | if(verbose) { 615 | mbp_log(LOG_INFO, "Sleeping for 2 seconds to get first temp delta"); 616 | } 617 | sleep(2); 618 | 619 | while(1) { 620 | old_temp = new_temp; 621 | new_temp = get_temp(sensors); 622 | 623 | fan = fans; 624 | 625 | while(fan != NULL) { 626 | fan_speed = fan->old_speed; 627 | 628 | if(new_temp >= max_temp && fan->old_speed != fan->fan_max_speed) { 629 | fan_speed = fan->fan_max_speed; 630 | } 631 | 632 | if(new_temp <= low_temp && fan_speed != fan->fan_min_speed) { 633 | fan_speed = fan->fan_min_speed; 634 | } 635 | 636 | temp_change = new_temp - old_temp; 637 | 638 | if(temp_change > 0 && new_temp > high_temp && new_temp < max_temp) { 639 | steps = ( new_temp - high_temp ) * ( new_temp - high_temp + 1 ) / 2; 640 | fan_speed = max( fan_speed, ceil(fan->fan_min_speed + steps * fan->step_up) ); 641 | } 642 | 643 | if(temp_change < 0 && new_temp > low_temp && new_temp < max_temp) { 644 | steps = ( max_temp - new_temp ) * ( max_temp - new_temp + 1 ) / 2; 645 | fan_speed = min( fan_speed, floor(fan->fan_max_speed - steps * fan->step_down) ); 646 | } 647 | 648 | if(verbose) { 649 | mbp_log(LOG_INFO, "Old Temp: %d New Temp: %d Fan: %s Speed: %d", old_temp, new_temp, fan->label, fan_speed); 650 | } 651 | 652 | set_fan_speed(fan, fan_speed); 653 | fan = fan->next; 654 | } 655 | 656 | if(verbose) { 657 | mbp_log(LOG_INFO, "Sleeping for %d seconds", polling_interval); 658 | } 659 | 660 | time_t before_sleep = time(NULL); 661 | 662 | // call nanosleep instead of sleep to avoid rt_sigprocmask and 663 | // rt_sigaction 664 | struct timespec ts; 665 | ts.tv_sec = polling_interval; 666 | ts.tv_nsec = 0; 667 | nanosleep(&ts, NULL); 668 | 669 | time_t after_sleep = time(NULL); 670 | if(after_sleep - before_sleep > 2 * polling_interval) { 671 | mbp_log(LOG_INFO, "Clock skew detected - slept for %ld seconds but expected %d", after_sleep - before_sleep, polling_interval); 672 | set_fans_man(fans); 673 | goto recalibrate; 674 | } 675 | } 676 | } -------------------------------------------------------------------------------- /src/mbpfan.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2010 Allan McRae 3 | * Modifications (2012-present) by Daniel Graziotin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | */ 16 | 17 | #ifndef _MBPFAN_H_ 18 | #define _MBPFAN_H_ 19 | 20 | #include 21 | 22 | /** Temperature Thresholds 23 | * low_temp - temperature below which fan speed will be at minimum 24 | * high_temp - fan will increase speed when higher than this temperature 25 | * max_temp - fan will run at full speed above this temperature */ 26 | extern int low_temp; 27 | extern int high_temp; 28 | extern int max_temp; 29 | 30 | /** Temperature polling interval 31 | * Default value was 10 (seconds) 32 | */ 33 | extern int polling_interval; 34 | 35 | /** Represents a Temperature sensor 36 | */ 37 | struct s_sensors; 38 | typedef struct s_sensors t_sensors; 39 | 40 | struct s_fans; 41 | typedef struct s_fans t_fans; 42 | 43 | char *smprintf(const char *fmt, ...) __attribute__((format (printf, 1, 2))); 44 | 45 | /** 46 | * Return true if the kernel is < 3.15.0 47 | */ 48 | bool is_legacy_sensors_path(); 49 | 50 | /** 51 | * Tries to use the settings located in 52 | * /etc/mbpfan.conf 53 | * If it fails, the default hardcoded settings are used 54 | */ 55 | void retrieve_settings(const char* settings_path, t_fans *fans); 56 | 57 | /** 58 | * Detect the sensors in /sys/devices/platform/coretemp.0/temp 59 | * and /sys/devices/platform/coretemp.1/temp etc 60 | * Return a linked list of t_sensors (first temperature detected) 61 | */ 62 | t_sensors *retrieve_sensors(); 63 | 64 | /** 65 | * Given a linked list of t_sensors, refresh their detected 66 | * temperature 67 | */ 68 | t_sensors *refresh_sensors(t_sensors *sensors); 69 | 70 | /** 71 | * Detect the fans in /sys/devices/platform/applesmc.768/ 72 | * Associate each fan to a sensor 73 | */ 74 | t_fans* retrieve_fans(); 75 | 76 | /** 77 | * Given a list of sensors with associated fans 78 | * Set them to manual control 79 | */ 80 | void set_fans_man(t_fans *fans); 81 | 82 | /** 83 | * Given a list of sensors with associated fans 84 | * Set them to automatic control 85 | */ 86 | void set_fans_auto(t_fans *fans); 87 | 88 | /** 89 | * Given a sensors with associated fans 90 | * Change their speed 91 | */ 92 | void set_fan_speed(t_fans* fan, int speed); 93 | 94 | /** 95 | * Given a list of fans set their minumum fan speed 96 | */ 97 | void set_fan_minimum_speed(t_fans* fans); 98 | /** 99 | * Return maximum CPU temp in degrees 100 | */ 101 | unsigned short get_temp(t_sensors* sensors); 102 | 103 | /** 104 | * Check if user has proper access and that required 105 | * kernel modules are available 106 | */ 107 | void check_requirements(const char* program_path); 108 | 109 | /** 110 | * Main Program 111 | */ 112 | void mbpfan(); 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /src/settings.c: -------------------------------------------------------------------------------- 1 | /* 2 | * settings version 1.0.1 3 | * 4 | * ANSI C implementation for managing application settings. 5 | * 6 | * Version History: 7 | * 1.0.0 (2009) - Initial release 8 | * 1.0.1 (2010) - Fixed small memory leak in settings_delete 9 | * (Thanks to Edwin van den Oetelaar) 10 | * 1.0.2 (2011) - Adapted code for new strmap API 11 | * 12 | * settings.c 13 | * 14 | * Copyright (c) 2009-2011 Per Ola Kristensson. 15 | * 16 | * Per Ola Kristensson 17 | * Inference Group, Department of Physics 18 | * University of Cambridge 19 | * Cavendish Laboratory 20 | * JJ Thomson Avenue 21 | * CB3 0HE Cambridge 22 | * United Kingdom 23 | * 24 | * settings is free software: you can redistribute it and/or modify 25 | * it under the terms of the GNU Lesser General Public License as published by 26 | * the Free Software Foundation, either version 3 of the License, or 27 | * (at your option) any later version. 28 | * 29 | * settings is distributed in the hope that it will be useful, 30 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | * GNU Lesser General Public License for more details. 33 | * 34 | * You should have received a copy of the GNU Lesser General Public License 35 | * along with settings. If not, see . 36 | */ 37 | #include "settings.h" 38 | 39 | #define MAX_SECTIONCHARS 256 40 | #define MAX_KEYCHARS 256 41 | #define MAX_VALUECHARS 256 42 | #define MAX_LINECHARS (MAX_KEYCHARS + MAX_VALUECHARS + 10) 43 | 44 | #define COMMENT_CHAR '#' 45 | #define SECTION_START_CHAR '[' 46 | #define SECTION_END_CHAR ']' 47 | #define KEY_VALUE_SEPARATOR_CHAR '=' 48 | 49 | #define DEFAULT_STRMAP_CAPACITY 256 50 | 51 | typedef struct Section Section; 52 | typedef struct ParseState ParseState; 53 | 54 | struct Settings { 55 | Section *sections; 56 | unsigned int section_count; 57 | }; 58 | 59 | struct Section { 60 | char *name; 61 | StrMap *map; 62 | }; 63 | 64 | struct ParseState { 65 | char *current_section; 66 | unsigned int current_section_n; 67 | int has_section; 68 | }; 69 | 70 | enum ConvertMode { 71 | CONVERT_MODE_INT, 72 | CONVERT_MODE_LONG, 73 | CONVERT_MODE_DOUBLE, 74 | }; 75 | 76 | typedef enum ConvertMode ConvertMode; 77 | 78 | static void trim_str(const char *str, char *out_buf); 79 | static int parse_str(Settings *settings, char *str, ParseState *parse_state); 80 | static int is_blank_char(char c); 81 | static int is_blank_str(const char *str); 82 | static int is_comment_str(const char *str); 83 | static int is_section_str(const char *str); 84 | static int is_key_value_str(const char *str); 85 | static int is_key_without_value_str(const char *str); 86 | static const char * get_token(char *str, char delim, char **last); 87 | static int get_section_from_str(const char *str, char *out_buf, unsigned int out_buf_n); 88 | static int get_key_value_from_str(const char *str, char *out_buf1, unsigned int out_buf1_n, char *out_buf2, unsigned int out_buf2_n); 89 | static int get_key_without_value_from_str(const char *str, char *out_buf, unsigned int out_buf_n); 90 | static int get_converted_value(const Settings *settings, const char *section, const char *key, ConvertMode mode, void *out); 91 | static int get_converted_tuple(const Settings *settings, const char *section, const char *key, char delim, ConvertMode mode, void *out, unsigned int n_out); 92 | static Section * get_section(Section *sections, unsigned int n, const char *name); 93 | static void enum_map(const char *key, const char *value, const void *obj); 94 | 95 | Settings * settings_new() 96 | { 97 | Settings *settings; 98 | 99 | settings = (Settings*)malloc(sizeof(Settings)); 100 | 101 | if (settings == NULL) { 102 | return NULL; 103 | } 104 | 105 | settings->section_count = 0; 106 | settings->sections = NULL; 107 | return settings; 108 | } 109 | 110 | void settings_delete(Settings *settings) 111 | { 112 | unsigned int i, n; 113 | Section *section; 114 | 115 | if (settings == NULL) { 116 | return; 117 | } 118 | 119 | section = settings->sections; 120 | n = settings->section_count; 121 | i = 0; 122 | 123 | while (i < n) { 124 | sm_delete(section->map); 125 | 126 | if (section->name != NULL) { 127 | free(section->name); 128 | } 129 | 130 | section++; 131 | i++; 132 | } 133 | 134 | free(settings->sections); 135 | free(settings); 136 | } 137 | 138 | Settings * settings_open(FILE *stream) 139 | { 140 | Settings *settings; 141 | char buf[MAX_LINECHARS]; 142 | char trimmed_buf[MAX_LINECHARS]; 143 | char section_buf[MAX_LINECHARS]; 144 | ParseState parse_state; 145 | 146 | if (stream == NULL) { 147 | return NULL; 148 | } 149 | 150 | settings = settings_new(); 151 | 152 | if (settings == NULL) { 153 | return NULL; 154 | } 155 | 156 | parse_state.current_section = section_buf; 157 | parse_state.current_section_n = sizeof(section_buf); 158 | parse_state.has_section = 0; 159 | trim_str("", trimmed_buf); 160 | 161 | while (fgets(buf, MAX_LINECHARS, stream) != NULL) { 162 | trim_str(buf, trimmed_buf); 163 | 164 | if (!parse_str(settings, trimmed_buf, &parse_state)) { 165 | return NULL; 166 | } 167 | } 168 | 169 | return settings; 170 | } 171 | 172 | int settings_save(const Settings *settings, FILE *stream) 173 | { 174 | unsigned int i, n; 175 | Section *section; 176 | char buf[MAX_LINECHARS]; 177 | 178 | if (settings == NULL) { 179 | return 0; 180 | } 181 | 182 | if (stream == NULL) { 183 | return 0; 184 | } 185 | 186 | section = settings->sections; 187 | n = settings->section_count; 188 | i = 0; 189 | 190 | while (i < n) { 191 | sprintf(buf, "[%s]\n", section->name); 192 | fputs(buf, stream); 193 | sm_enum(section->map, enum_map, stream); 194 | section++; 195 | i++; 196 | fputs("\n", stream); 197 | } 198 | 199 | return 0; 200 | } 201 | 202 | int settings_get(const Settings *settings, const char *section, const char *key, char *out_buf, unsigned int n_out_buf) 203 | { 204 | Section *s; 205 | 206 | if (settings == NULL) { 207 | return 0; 208 | } 209 | 210 | s = get_section(settings->sections, settings->section_count, section); 211 | 212 | if (s == NULL) { 213 | return 0; 214 | } 215 | 216 | return sm_get(s->map, key, out_buf, n_out_buf); 217 | } 218 | 219 | int settings_get_int(const Settings *settings, const char *section, const char *key) 220 | { 221 | int i; 222 | 223 | if (get_converted_value(settings, section, key, CONVERT_MODE_INT, &i)) { 224 | return i; 225 | } 226 | 227 | return 0; 228 | } 229 | 230 | long settings_get_long(const Settings *settings, const char *section, const char *key) 231 | { 232 | long l; 233 | 234 | if (get_converted_value(settings, section, key, CONVERT_MODE_LONG, &l)) { 235 | return l; 236 | } 237 | 238 | return 0; 239 | } 240 | 241 | double settings_get_double(const Settings *settings, const char *section, const char *key) 242 | { 243 | double d; 244 | 245 | if (get_converted_value(settings, section, key, CONVERT_MODE_DOUBLE, &d)) { 246 | return d; 247 | } 248 | 249 | return 0; 250 | } 251 | 252 | int settings_get_int_tuple(const Settings *settings, const char *section, const char *key, int *out, unsigned int n_out) 253 | { 254 | return get_converted_tuple(settings, section, key, ',', CONVERT_MODE_INT, out, n_out); 255 | } 256 | 257 | long settings_get_long_tuple(const Settings *settings, const char *section, const char *key, long *out, unsigned int n_out) 258 | { 259 | return get_converted_tuple(settings, section, key, ',', CONVERT_MODE_LONG, out, n_out); 260 | } 261 | 262 | double settings_get_double_tuple(const Settings *settings, const char *section, const char *key, double *out, unsigned int n_out) 263 | { 264 | return get_converted_tuple(settings, section, key, ',', CONVERT_MODE_DOUBLE, out, n_out); 265 | } 266 | 267 | int settings_set(Settings *settings, const char *section, const char *key, const char *value) 268 | { 269 | Section *s; 270 | 271 | if (settings == NULL) { 272 | return 0; 273 | } 274 | 275 | if (section == NULL || key == NULL || value == NULL) { 276 | return 0; 277 | } 278 | 279 | if (strlen(section) == 0) { 280 | return 0; 281 | } 282 | 283 | /* Get a pointer to the section */ 284 | s = get_section(settings->sections, settings->section_count, section); 285 | 286 | if (s == NULL) { 287 | /* The section is not created---create it */ 288 | s = (Section*)realloc(settings->sections, (settings->section_count + 1) * sizeof(Section)); 289 | 290 | if (s == NULL) { 291 | return 0; 292 | } 293 | 294 | settings->sections = s; 295 | settings->section_count++; 296 | s = &(settings->sections[settings->section_count - 1]); 297 | s->map = sm_new(DEFAULT_STRMAP_CAPACITY); 298 | 299 | if (s->map == NULL) { 300 | free(s); 301 | return 0; 302 | } 303 | 304 | s->name = (char*)malloc((strlen(section) + 1) * sizeof(char)); 305 | 306 | if (s->name == NULL) { 307 | sm_delete(s->map); 308 | free(s); 309 | return 0; 310 | } 311 | 312 | strcpy(s->name, section); 313 | } 314 | 315 | return sm_put(s->map, key, value); 316 | } 317 | 318 | int settings_section_get_count(const Settings *settings, const char *section) 319 | { 320 | Section *sect; 321 | 322 | if (settings == NULL) { 323 | return 0; 324 | } 325 | 326 | sect = get_section(settings->sections, settings->section_count, section); 327 | 328 | if (sect == NULL) { 329 | return 0; 330 | } 331 | 332 | return sm_get_count(sect->map); 333 | } 334 | 335 | int settings_section_enum(const Settings *settings, const char *section, settings_section_enum_func enum_func, const void *obj) 336 | { 337 | Section *sect; 338 | 339 | sect = get_section(settings->sections, settings->section_count, section); 340 | 341 | if (sect == NULL) { 342 | return 0; 343 | } 344 | 345 | return sm_enum(sect->map, enum_func, obj); 346 | } 347 | 348 | /* Copies a trimmed variant without leading and trailing blank characters 349 | * of the input string into the output buffer. The output buffer is assumed 350 | * to be large enough to contain the entire input string. 351 | */ 352 | static void trim_str(const char *str, char *out_buf) 353 | { 354 | unsigned int len; 355 | const char *s0; 356 | 357 | while (*str != '\0' && is_blank_char(*str)) { 358 | str++; 359 | } 360 | 361 | s0 = str; 362 | len = 0; 363 | 364 | while (*str != '\0') { 365 | len++; 366 | str++; 367 | } 368 | 369 | if (len > 0) { 370 | str--; 371 | } 372 | 373 | while (is_blank_char(*str)) { 374 | str--; 375 | len--; 376 | } 377 | 378 | memcpy(out_buf, s0, len); 379 | out_buf[len] = '\0'; 380 | } 381 | 382 | /* Parses a single input string and updates the provided settings object. 383 | * The given parse state may be updated following a call. It is assumed this 384 | * function is called in repeated succession for each input line read. The 385 | * provided parse state should be initialized to the following before this 386 | * function is called for the first time for an intended parse: 387 | * 388 | * parse_state->current_section: a pre-allocated character buffer this function 389 | * can read and write to 390 | * parse_state->current_section_n: sizeof(parse_state->current_section) 391 | * parse_state->has_section: 0 (false) 392 | */ 393 | static int parse_str(Settings *settings, char *str, ParseState *parse_state) 394 | { 395 | char buf[MAX_LINECHARS]; 396 | char buf1[MAX_LINECHARS]; 397 | char buf2[MAX_LINECHARS]; 398 | int result; 399 | 400 | if (*str == '\0') { 401 | return 1; 402 | 403 | } else if (is_blank_str(str)) { 404 | return 1; 405 | 406 | } else if (is_comment_str(str)) { 407 | return 1; 408 | 409 | } else if (is_section_str(str)) { 410 | result = get_section_from_str(str, buf, sizeof(buf)); 411 | 412 | if (!result) { 413 | return 0; 414 | } 415 | 416 | if (strlen(buf) + 1 > parse_state->current_section_n) { 417 | return 0; 418 | } 419 | 420 | strcpy(parse_state->current_section, buf); 421 | parse_state->has_section = 1; 422 | return 1; 423 | 424 | } else if (is_key_value_str(str)) { 425 | result = get_key_value_from_str(str, buf1, sizeof(buf1), buf2, sizeof(buf2)); 426 | 427 | if (!result) { 428 | return 0; 429 | } 430 | 431 | if (!parse_state->has_section) { 432 | return 0; 433 | } 434 | 435 | return settings_set(settings, parse_state->current_section, buf1, buf2); 436 | 437 | } else if (is_key_without_value_str(str)) { 438 | result = get_key_without_value_from_str(str, buf, sizeof(buf)); 439 | 440 | if (!result) { 441 | return 0; 442 | } 443 | 444 | if (!parse_state->has_section) { 445 | return 0; 446 | } 447 | 448 | return settings_set(settings, parse_state->current_section, buf, ""); 449 | 450 | } else { 451 | return 0; 452 | } 453 | } 454 | 455 | /* Returns true if the input character is blank, 456 | * false otherwise. 457 | */ 458 | static int is_blank_char(char c) 459 | { 460 | return c == ' ' || c == '\t' || c == '\r' || c == '\n'; 461 | } 462 | 463 | /* Returns true if the input string is blank, 464 | * false otherwise. 465 | */ 466 | static int is_blank_str(const char *str) 467 | { 468 | while (*str != '\0') { 469 | if (!is_blank_char(*str)) { 470 | return 0; 471 | } 472 | 473 | str++; 474 | } 475 | 476 | return 1; 477 | } 478 | 479 | /* Returns true if the input string denotes a comment, 480 | * false otherwise. 481 | */ 482 | static int is_comment_str(const char *str) 483 | { 484 | if (*str == COMMENT_CHAR) { 485 | /* To be a comment the first character must be the 486 | * comment character. 487 | */ 488 | return 1; 489 | } 490 | 491 | return 0; 492 | } 493 | 494 | /* Returns true if the input string denotes a section name, 495 | * false otherwise. 496 | */ 497 | static int is_section_str(const char *str) 498 | { 499 | if (*str != SECTION_START_CHAR) { 500 | /* The first character must be the section start character */ 501 | return 0; 502 | } 503 | 504 | while (*str != '\0' && *str != SECTION_END_CHAR) { 505 | str++; 506 | } 507 | 508 | if (*str != SECTION_END_CHAR) { 509 | /* The section end character must be present somewhere thereafter */ 510 | return 0; 511 | } 512 | 513 | return 1; 514 | } 515 | 516 | /* Returns true if the input string denotes a key-value pair, 517 | * false otherwise. 518 | */ 519 | static int is_key_value_str(const char *str) 520 | { 521 | if (*str == KEY_VALUE_SEPARATOR_CHAR) { 522 | /* It is illegal to start with the key-value separator */ 523 | return 0; 524 | } 525 | 526 | while (*str != '\0' && *str != KEY_VALUE_SEPARATOR_CHAR) { 527 | str++; 528 | } 529 | 530 | if (*str != KEY_VALUE_SEPARATOR_CHAR) { 531 | /* The key-value separator must be present after the key part */ 532 | return 0; 533 | } 534 | 535 | return 1; 536 | } 537 | 538 | /* Returns true if the input string denotes a key without a value, 539 | * false otherwise. 540 | */ 541 | static int is_key_without_value_str(const char *str) 542 | { 543 | if (*str == KEY_VALUE_SEPARATOR_CHAR) { 544 | /* It is illegal to start with the key-value separator */ 545 | return 0; 546 | } 547 | 548 | while (*str != '\0' && *str != KEY_VALUE_SEPARATOR_CHAR) { 549 | str++; 550 | } 551 | 552 | if (*str == KEY_VALUE_SEPARATOR_CHAR) { 553 | /* The key-value separator must not be present after the key part */ 554 | return 0; 555 | } 556 | 557 | return 1; 558 | } 559 | 560 | /* 561 | * Parses a section name from an input string. The input string is assumed to 562 | * already have been identified as a valid input string denoting a section name. 563 | */ 564 | static int get_section_from_str(const char *str, char *out_buf, unsigned int out_buf_n) 565 | { 566 | unsigned int count; 567 | 568 | count = 0; 569 | /* Jump past the section begin character */ 570 | str++; 571 | 572 | while (*str != '\0' && *str != SECTION_END_CHAR) { 573 | /* Read in the section name into the output buffer */ 574 | if (count == out_buf_n) { 575 | return 0; 576 | } 577 | 578 | *out_buf = *str; 579 | out_buf++; 580 | str++; 581 | count++; 582 | } 583 | 584 | /* Terminate the output buffer */ 585 | if (count == out_buf_n) { 586 | return 0; 587 | } 588 | 589 | *out_buf = '\0'; 590 | return 1; 591 | } 592 | 593 | /* 594 | * Parses a key and value from an input string. The input string is assumed to 595 | * already have been identified as a valid input string denoting a key-value pair. 596 | */ 597 | static int get_key_value_from_str(const char *str, char *out_buf1, unsigned int out_buf1_n, char *out_buf2, unsigned int out_buf2_n) 598 | { 599 | unsigned int count1; 600 | unsigned int count2; 601 | 602 | count1 = 0; 603 | count2 = 0; 604 | 605 | /* Read the key value from the input string and write it sequentially 606 | * to the first output buffer by walking the input string until we either hit 607 | * the null-terminator or the key-value separator. 608 | */ 609 | while (*str != '\0' && *str != KEY_VALUE_SEPARATOR_CHAR) { 610 | /* Ensure the first output buffer is large enough. */ 611 | if (count1 == out_buf1_n) { 612 | return 0; 613 | } 614 | 615 | /* Copy the character to the first output buffer */ 616 | *out_buf1 = *str; 617 | out_buf1++; 618 | str++; 619 | count1++; 620 | } 621 | 622 | /* Terminate the first output buffer */ 623 | if (count1 == out_buf1_n) { 624 | return 0; 625 | } 626 | 627 | *out_buf1 = '\0'; 628 | 629 | /* Now trace the first input buffer backwards until we hit a non-blank character */ 630 | while (is_blank_char(*(out_buf1 - 1))) { 631 | out_buf1--; 632 | } 633 | 634 | *out_buf1 = '\0'; 635 | 636 | /* Try to proceed one more character, past the last read key-value 637 | * delimiter, in the input string. 638 | */ 639 | if (*str != '\0') { 640 | str++; 641 | } 642 | 643 | /* Now find start of the value in the input string by walking the input 644 | * string until we either hit the null-terminator or a blank character. 645 | */ 646 | while (*str != '\0' && is_blank_char(*str)) { 647 | str++; 648 | } 649 | 650 | while (*str != '\0') { 651 | /* Fail if there is a possibility that we are overwriting the second 652 | * input buffer. 653 | */ 654 | if (count2 == out_buf2_n) { 655 | return 0; 656 | } 657 | 658 | /* Copy the character to the second output buffer */ 659 | *out_buf2 = *str; 660 | out_buf2++; 661 | str++; 662 | count2++; 663 | } 664 | 665 | /* Terminate the second output buffer */ 666 | if (count2 == out_buf2_n) { 667 | return 0; 668 | } 669 | 670 | *out_buf2 = '\0'; 671 | return 1; 672 | } 673 | 674 | /* 675 | * Parses a key from an input string. The input string is assumed to already 676 | * have been identified as a valid input string denoting a key without a value. 677 | */ 678 | static int get_key_without_value_from_str(const char *str, char *out_buf, unsigned int out_buf_n) 679 | { 680 | unsigned int count; 681 | 682 | count = 0; 683 | 684 | /* Now read the key value from the input string and write it sequentially 685 | * to the output buffer by walking the input string until we either hit 686 | * the null-terminator or the key-value separator. 687 | */ 688 | while (*str != '\0') { 689 | /* Ensure the output buffer is large enough. */ 690 | if (count == out_buf_n) { 691 | return 0; 692 | } 693 | 694 | /* Copy the character to the input buffer */ 695 | *out_buf = *str; 696 | out_buf++; 697 | str++; 698 | count++; 699 | } 700 | 701 | /* Terminate the output buffer */ 702 | if (count == out_buf_n) { 703 | return 0; 704 | } 705 | 706 | *out_buf = '\0'; 707 | return 1; 708 | } 709 | 710 | /* Returns a pointer to the next token in the input string delimited 711 | * by the specified delimiter or null if no such token exist. The provided 712 | * last pointer will be changed to point one position after the pointed 713 | * token. The currently ouputted token will be null-terminated. 714 | * 715 | * An idiom for tokenizing a (in this case, comma-separated) string is: 716 | * 717 | * char test_string[] = "Token1,Token2,Token3"; 718 | * char token[255]; 719 | * char *str; 720 | * 721 | * str = test_string; 722 | * while ((token = get_token(str, ',', &str) != NULL) { 723 | * printf("token: %s", token); 724 | * } 725 | */ 726 | static const char * get_token(char *str, char delim, char **last) 727 | { 728 | 729 | char *s0; 730 | 731 | s0 = str; 732 | 733 | /* If we hit the null-terminator the string 734 | * is exhausted and another token does not 735 | * exist. 736 | */ 737 | if (*str == '\0') { 738 | return NULL; 739 | } 740 | 741 | /* Walk the string until we encounter a 742 | * null-terminator or the delimiter. 743 | */ 744 | while (*str != '\0' && *str != delim) { 745 | str++; 746 | } 747 | 748 | /* Terminate the return token, if necessary */ 749 | if (*str != '\0') { 750 | *str = '\0'; 751 | str++; 752 | } 753 | 754 | *last = str; 755 | return s0; 756 | } 757 | 758 | /* Returns a converted value pointed to by the provided key in the given section. 759 | * The mode specifies which conversion takes place and dictates what value out 760 | * is pointing to. The value out is pointing to will be replaced by the converted 761 | * value assuming conversion is succesful. The function returns 1 if conversion 762 | * is succsessful and 0 if the convertion could not be carried out. 763 | */ 764 | static int get_converted_value(const Settings *settings, const char *section, const char *key, ConvertMode mode, void *out) 765 | { 766 | char value[MAX_VALUECHARS]; 767 | 768 | if (!settings_get(settings, section, key, value, MAX_VALUECHARS)) { 769 | return 0; 770 | } 771 | 772 | switch (mode) { 773 | case CONVERT_MODE_INT: 774 | *((int *)out) = atoi(value); 775 | return 1; 776 | 777 | case CONVERT_MODE_LONG: 778 | *((long *)out) = atol(value); 779 | return 1; 780 | 781 | case CONVERT_MODE_DOUBLE: 782 | *((double *)out) = atof(value); 783 | return 1; 784 | } 785 | 786 | return 0; 787 | } 788 | 789 | /* Returns a converted tuple pointed to by the provided key in the given section. 790 | * The tuple is created by splitting the value by the supplied delimiter and then 791 | * converting each token after the split according to the specified mode. 792 | * The array out is pointing to will be replaced by the converted tuple 793 | * assuming conversion is succesful. The function returns 1 if conversion 794 | * is succsessful and 0 if the convertion could not be carried out. 795 | */ 796 | static int get_converted_tuple(const Settings *settings, const char *section, const char *key, char delim, ConvertMode mode, void *out, unsigned int n_out) 797 | { 798 | unsigned int count; 799 | const char *token; 800 | static char value[MAX_VALUECHARS]; 801 | char *v; 802 | 803 | if (out == NULL) { 804 | return 0; 805 | } 806 | 807 | if (n_out == 0) { 808 | return 0; 809 | } 810 | 811 | if (!settings_get(settings, section, key, value, MAX_VALUECHARS)) { 812 | return 0; 813 | } 814 | 815 | v = value; 816 | count = 0; 817 | 818 | /* Walk over all tokens in the value, and convert them and assign them 819 | * to the output array as specified by the mode. 820 | */ 821 | while ((token = get_token(v, delim, &v)) != NULL && count < n_out) { 822 | switch (mode) { 823 | case CONVERT_MODE_INT: 824 | ((int *)out)[count] = atoi(token); 825 | break; 826 | 827 | case CONVERT_MODE_LONG: 828 | ((long *)out)[count] = atol(token); 829 | break; 830 | 831 | case CONVERT_MODE_DOUBLE: 832 | ((double *)out)[count] = atof(token); 833 | break; 834 | 835 | default: 836 | return 0; 837 | } 838 | 839 | count++; 840 | } 841 | 842 | return 1; 843 | } 844 | 845 | /* Returns a pointer to the section or null if the named section does not 846 | * exist. 847 | */ 848 | static Section * get_section(Section *sections, unsigned int n, const char *name) 849 | { 850 | unsigned int i; 851 | Section *section; 852 | 853 | if (name == NULL) { 854 | return NULL; 855 | } 856 | 857 | section = sections; 858 | i = 0; 859 | 860 | while (i < n) { 861 | if (strcmp(section->name, name) == 0) { 862 | return section; 863 | } 864 | 865 | section++; 866 | i++; 867 | } 868 | 869 | return NULL; 870 | } 871 | 872 | /* Callback function that is passed into the enumeration function in the 873 | * string map. It casts the passed into object into a FILE pointer and 874 | * writes out the key and value to the file. 875 | */ 876 | static void enum_map(const char *key, const char *value, const void *obj) 877 | { 878 | FILE *stream; 879 | char buf[MAX_LINECHARS]; 880 | 881 | if (key == NULL || value == NULL) { 882 | return; 883 | } 884 | 885 | if (obj == NULL) { 886 | return; 887 | } 888 | 889 | stream = (FILE *)obj; 890 | 891 | if (strlen(key) < MAX_KEYCHARS && strlen(value) < MAX_VALUECHARS) { 892 | sprintf(buf, "%s%c%s\n", key, KEY_VALUE_SEPARATOR_CHAR, value); 893 | fputs(buf, stream); 894 | } 895 | } 896 | 897 | /* 898 | 899 | GNU LESSER GENERAL PUBLIC LICENSE 900 | Version 3, 29 June 2007 901 | 902 | Copyright (C) 2007 Free Software Foundation, Inc. 903 | Everyone is permitted to copy and distribute verbatim copies 904 | of this license document, but changing it is not allowed. 905 | 906 | 907 | This version of the GNU Lesser General Public License incorporates 908 | the terms and conditions of version 3 of the GNU General Public 909 | License, supplemented by the additional permissions listed below. 910 | 911 | 0. Additional Definitions. 912 | 913 | As used herein, "this License" refers to version 3 of the GNU Lesser 914 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 915 | General Public License. 916 | 917 | "The Library" refers to a covered work governed by this License, 918 | other than an Application or a Combined Work as defined below. 919 | 920 | An "Application" is any work that makes use of an interface provided 921 | by the Library, but which is not otherwise based on the Library. 922 | Defining a subclass of a class defined by the Library is deemed a mode 923 | of using an interface provided by the Library. 924 | 925 | A "Combined Work" is a work produced by combining or linking an 926 | Application with the Library. The particular version of the Library 927 | with which the Combined Work was made is also called the "Linked 928 | Version". 929 | 930 | The "Minimal Corresponding Source" for a Combined Work means the 931 | Corresponding Source for the Combined Work, excluding any source code 932 | for portions of the Combined Work that, considered in isolation, are 933 | based on the Application, and not on the Linked Version. 934 | 935 | The "Corresponding Application Code" for a Combined Work means the 936 | object code and/or source code for the Application, including any data 937 | and utility programs needed for reproducing the Combined Work from the 938 | Application, but excluding the System Libraries of the Combined Work. 939 | 940 | 1. Exception to Section 3 of the GNU GPL. 941 | 942 | You may convey a covered work under sections 3 and 4 of this License 943 | without being bound by section 3 of the GNU GPL. 944 | 945 | 2. Conveying Modified Versions. 946 | 947 | If you modify a copy of the Library, and, in your modifications, a 948 | facility refers to a function or data to be supplied by an Application 949 | that uses the facility (other than as an argument passed when the 950 | facility is invoked), then you may convey a copy of the modified 951 | version: 952 | 953 | a) under this License, provided that you make a good faith effort to 954 | ensure that, in the event an Application does not supply the 955 | function or data, the facility still operates, and performs 956 | whatever part of its purpose remains meaningful, or 957 | 958 | b) under the GNU GPL, with none of the additional permissions of 959 | this License applicable to that copy. 960 | 961 | 3. Object Code Incorporating Material from Library Header Files. 962 | 963 | The object code form of an Application may incorporate material from 964 | a header file that is part of the Library. You may convey such object 965 | code under terms of your choice, provided that, if the incorporated 966 | material is not limited to numerical parameters, data structure 967 | layouts and accessors, or small macros, inline functions and templates 968 | (ten or fewer lines in length), you do both of the following: 969 | 970 | a) Give prominent notice with each copy of the object code that the 971 | Library is used in it and that the Library and its use are 972 | covered by this License. 973 | 974 | b) Accompany the object code with a copy of the GNU GPL and this license 975 | document. 976 | 977 | 4. Combined Works. 978 | 979 | You may convey a Combined Work under terms of your choice that, 980 | taken together, effectively do not restrict modification of the 981 | portions of the Library contained in the Combined Work and reverse 982 | engineering for debugging such modifications, if you also do each of 983 | the following: 984 | 985 | a) Give prominent notice with each copy of the Combined Work that 986 | the Library is used in it and that the Library and its use are 987 | covered by this License. 988 | 989 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 990 | document. 991 | 992 | c) For a Combined Work that displays copyright notices during 993 | execution, include the copyright notice for the Library among 994 | these notices, as well as a reference directing the user to the 995 | copies of the GNU GPL and this license document. 996 | 997 | d) Do one of the following: 998 | 999 | 0) Convey the Minimal Corresponding Source under the terms of this 1000 | License, and the Corresponding Application Code in a form 1001 | suitable for, and under terms that permit, the user to 1002 | recombine or relink the Application with a modified version of 1003 | the Linked Version to produce a modified Combined Work, in the 1004 | manner specified by section 6 of the GNU GPL for conveying 1005 | Corresponding Source. 1006 | 1007 | 1) Use a suitable shared library mechanism for linking with the 1008 | Library. A suitable mechanism is one that (a) uses at run time 1009 | a copy of the Library already present on the user's computer 1010 | system, and (b) will operate properly with a modified version 1011 | of the Library that is interface-compatible with the Linked 1012 | Version. 1013 | 1014 | e) Provide Installation Information, but only if you would otherwise 1015 | be required to provide such information under section 6 of the 1016 | GNU GPL, and only to the extent that such information is 1017 | necessary to install and execute a modified version of the 1018 | Combined Work produced by recombining or relinking the 1019 | Application with a modified version of the Linked Version. (If 1020 | you use option 4d0, the Installation Information must accompany 1021 | the Minimal Corresponding Source and Corresponding Application 1022 | Code. If you use option 4d1, you must provide the Installation 1023 | Information in the manner specified by section 6 of the GNU GPL 1024 | for conveying Corresponding Source.) 1025 | 1026 | 5. Combined Libraries. 1027 | 1028 | You may place library facilities that are a work based on the 1029 | Library side by side in a single library together with other library 1030 | facilities that are not Applications and are not covered by this 1031 | License, and convey such a combined library under terms of your 1032 | choice, if you do both of the following: 1033 | 1034 | a) Accompany the combined library with a copy of the same work based 1035 | on the Library, uncombined with any other library facilities, 1036 | conveyed under the terms of this License. 1037 | 1038 | b) Give prominent notice with the combined library that part of it 1039 | is a work based on the Library, and explaining where to find the 1040 | accompanying uncombined form of the same work. 1041 | 1042 | 6. Revised Versions of the GNU Lesser General Public License. 1043 | 1044 | The Free Software Foundation may publish revised and/or new versions 1045 | of the GNU Lesser General Public License from time to time. Such new 1046 | versions will be similar in spirit to the present version, but may 1047 | differ in detail to address new problems or concerns. 1048 | 1049 | Each version is given a distinguishing version number. If the 1050 | Library as you received it specifies that a certain numbered version 1051 | of the GNU Lesser General Public License "or any later version" 1052 | applies to it, you have the option of following the terms and 1053 | conditions either of that published version or of any later version 1054 | published by the Free Software Foundation. If the Library as you 1055 | received it does not specify a version number of the GNU Lesser 1056 | General Public License, you may choose any version of the GNU Lesser 1057 | General Public License ever published by the Free Software Foundation. 1058 | 1059 | If the Library as you received it specifies that a proxy can decide 1060 | whether future versions of the GNU Lesser General Public License shall 1061 | apply, that proxy's public statement of acceptance of any version is 1062 | permanent authorization for you to choose that version for the 1063 | Library. 1064 | 1065 | */ 1066 | -------------------------------------------------------------------------------- /src/settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * settings version 1.0.0 3 | * 4 | * ANSI C implementation for managing application settings. 5 | * 6 | * settings.h 7 | * 8 | * Copyright (c) 2009 Per Ola Kristensson. 9 | * 10 | * Per Ola Kristensson 11 | * Inference Group, Department of Physics 12 | * University of Cambridge 13 | * Cavendish Laboratory 14 | * JJ Thomson Avenue 15 | * CB3 0HE Cambridge 16 | * United Kingdom 17 | * 18 | * settings is free software: you can redistribute it and/or modify 19 | * it under the terms of the GNU Lesser General Public License as published by 20 | * the Free Software Foundation, either version 3 of the License, or 21 | * (at your option) any later version. 22 | * 23 | * settings is distributed in the hope that it will be useful, 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | * GNU Lesser General Public License for more details. 27 | * 28 | * You should have received a copy of the GNU Lesser General Public License 29 | * along with settings. If not, see . 30 | */ 31 | #ifndef _SETTINGS_H_ 32 | #define _SETTINGS_H_ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "strmap.h" 40 | 41 | #ifdef __cplusplus 42 | extern "C" 43 | { 44 | #endif 45 | 46 | typedef struct Settings Settings; 47 | 48 | /* 49 | * This callback function is called once per key-value when enumerating 50 | * all keys inside a section. 51 | * 52 | * Parameters: 53 | * 54 | * key: A pointer to a null-terminated C string. The string must not 55 | * be modified by the client. 56 | * 57 | * value: A pointer to a null-terminated C string. The string must 58 | * not be modified by the client. 59 | * 60 | * obj: A pointer to a client-specific object. This parameter may be 61 | * null. 62 | * 63 | * Return value: None. 64 | */ 65 | typedef void(*settings_section_enum_func)(const char *key, const char *value, const void *obj); 66 | 67 | /* 68 | * Creates a settings object. 69 | * 70 | * Return value: A pointer to a settings object, 71 | * or null if a new settings object could not be allocated. 72 | */ 73 | Settings * settings_new(); 74 | 75 | /* 76 | * Releases all memory held by a settings object. 77 | * 78 | * Parameters: 79 | * 80 | * settings: A pointer to a settings object. This parameter cannot be null. 81 | * If the supplied settings object has been previously released, the 82 | * behaviour of this function is undefined. 83 | * 84 | * Return value: None. 85 | */ 86 | void settings_delete(Settings *settings); 87 | 88 | /* 89 | * Constructs a settings object by loading settings in textual form 90 | * from the given stream. 91 | * 92 | * Parameters: 93 | * 94 | * settings: A pointer to a settings object. This parameter cannot be null. 95 | * 96 | * stream: A pointer to a stream. This parameter cannot be null. 97 | * 98 | * Return value: A pointer to a settings object, 99 | * or null if an error occurred. 100 | */ 101 | Settings * settings_open(FILE *stream); 102 | 103 | /* 104 | * Saves the current settings object in textual form to the given stream. 105 | * 106 | * Parameters: 107 | * 108 | * settings: A pointer to a settings object. This parameter cannot be null. 109 | * 110 | * stream: A pointer to a stream. This parameter cannot be null. 111 | * 112 | * Return value: 1 if the operation succeeded, 0 otherwise. 113 | */ 114 | int settings_save(const Settings *settings, FILE *stream); 115 | 116 | /* 117 | * Returns the value associated with the supplied key in the 118 | * provided section. 119 | * 120 | * Parameters: 121 | * 122 | * settings: A pointer to a settings object. This parameter cannot be null. 123 | * 124 | * section: A pointer to a null-terminated C string. This parameter cannot 125 | * be null. 126 | * 127 | * key: A pointer to a null-terminated C string. This parameter cannot 128 | * be null. 129 | * 130 | * out_buf: A pointer to an output buffer which will contain the value, 131 | * if it exists and fits into the buffer. 132 | * 133 | * n_out_buf: The size of the output buffer in bytes. 134 | * 135 | * Return value: If out_buf is set to null and n_out_buf is set to 0 the return 136 | * value will be the number of bytes required to store the value (if it exists) 137 | * and its null-terminator. For all other parameter configurations the return value 138 | * is 1 if an associated value was found and completely copied into the output buffer, 139 | * 0 otherwise. 140 | */ 141 | int settings_get(const Settings *settings, const char *section, const char *key, char *out_buf, unsigned int n_out_buf); 142 | 143 | /* 144 | * Returns the integer value associated with the supplied key in the 145 | * provided section. 146 | * 147 | * Parameters: 148 | * 149 | * settings: A pointer to a settings object. This parameter cannot be null. 150 | * 151 | * section: A pointer to a null-terminated C string. This parameter cannot 152 | * be null. 153 | * 154 | * key: A pointer to a null-terminated C string. This parameter cannot 155 | * be null. 156 | * 157 | * Return value: The integer value associated to the provided section and 158 | * key, or 0 if no such value exists. 159 | */ 160 | int settings_get_int(const Settings *settings, const char *section, const char *key); 161 | 162 | /* 163 | * Returns the long integer value associated with the supplied key in the 164 | * provided section. 165 | * 166 | * Parameters: 167 | * 168 | * settings: A pointer to a settings object. This parameter cannot be null. 169 | * 170 | * section: A pointer to a null-terminated C string. This parameter cannot 171 | * be null. 172 | * 173 | * key: A pointer to a null-terminated C string. This parameter cannot 174 | * be null. 175 | * 176 | * Return value: The long integer value associated to the provided section and 177 | * key, or 0 if no such value exists. 178 | */ 179 | long settings_get_long(const Settings *settings, const char *section, const char *key); 180 | 181 | /* 182 | * Returns the double value associated with the supplied key in the 183 | * provided section. 184 | * 185 | * Parameters: 186 | * 187 | * settings: A pointer to a settings object. This parameter cannot be null. 188 | * 189 | * section: A pointer to a null-terminated C string. This parameter cannot 190 | * be null. 191 | * 192 | * key: A pointer to a null-terminated C string. This parameter cannot 193 | * be null. 194 | * 195 | * Return value: The double value associated to the provided section and 196 | * key, or 0 if no such value exists. 197 | */ 198 | double settings_get_double(const Settings *settings, const char *section, const char *key); 199 | 200 | /* 201 | * Returns the integer tuple associated with the supplied key in the 202 | * provided section. 203 | * 204 | * Parameters: 205 | * 206 | * settings: A pointer to a settings object. This parameter cannot be null. 207 | * 208 | * section: A pointer to a null-terminated C string. This parameter cannot 209 | * be null. 210 | * 211 | * key: A pointer to a null-terminated C string. This parameter cannot 212 | * be null. 213 | * 214 | * out: A pointer to an output buffer. 215 | * 216 | * n_out: The maximum number of elements the output buffer can hold. 217 | * 218 | * Return value: 1 if the entire tuple was copied into the output buffer, 219 | * 0 otherwise. 220 | */ 221 | int settings_get_int_tuple(const Settings *settings, const char *section, const char *key, int *out, unsigned int n_out); 222 | 223 | /* 224 | * Returns the long tuple associated with the supplied key in the 225 | * provided section. 226 | * 227 | * Parameters: 228 | * 229 | * settings: A pointer to a settings object. This parameter cannot be null. 230 | * 231 | * section: A pointer to a null-terminated C string. This parameter cannot 232 | * be null. 233 | * 234 | * key: A pointer to a null-terminated C string. This parameter cannot 235 | * be null. 236 | * 237 | * out: A pointer to an output buffer. 238 | * 239 | * n_out: The maximum number of elements the output buffer can hold. 240 | * 241 | * Return value: 1 if the entire tuple was copied into the output buffer, 242 | * 0 otherwise. 243 | */ 244 | long settings_get_long_tuple(const Settings *settings, const char *section, const char *key, long *out, unsigned int n_out); 245 | 246 | /* 247 | * Returns the double tuple associated with the supplied key in the 248 | * provided section. 249 | * 250 | * Parameters: 251 | * 252 | * settings: A pointer to a settings object. This parameter cannot be null. 253 | * 254 | * section: A pointer to a null-terminated C string. This parameter cannot 255 | * be null. 256 | * 257 | * key: A pointer to a null-terminated C string. This parameter cannot 258 | * be null. 259 | * 260 | * out: A pointer to an output buffer. 261 | * 262 | * n_out: The maximum number of elements the output buffer can hold. 263 | * 264 | * Return value: 1 if the entire tuple was copied into the output buffer, 265 | * 0 otherwise. 266 | */ 267 | double settings_get_double_tuple(const Settings *settings, const char *section, const char *key, double *out, unsigned int n_out); 268 | 269 | /* 270 | * Associates a value with the supplied key in the provided section. 271 | * If the key is already associated with a value, the previous value 272 | * is replaced. 273 | * 274 | * Parameters: 275 | * 276 | * settings: A pointer to a settings object. This parameter cannot be null. 277 | * 278 | * section: A pointer to a null-terminated C string. This parameter cannot 279 | * be null. The string must have a string length > 0. The string will 280 | * be copied. 281 | * 282 | * key: A pointer to a null-terminated C string. This parameter 283 | * cannot be null. The string must have a string length > 0. The 284 | * string will be copied. 285 | * 286 | * value: A pointer to a null-terminated C string. This parameter 287 | * cannot be null. The string must have a string length > 0. The 288 | * string will be copied. 289 | * 290 | * Return value: 1 if the association succeeded, 0 otherwise. 291 | */ 292 | int settings_set(Settings *setting, const char *section, const char *key, const char *value); 293 | 294 | /* 295 | * Returns the number of associations between keys and values that exist 296 | * in the provided section. 297 | * 298 | * Parameters: 299 | * 300 | * settings: A pointer to a settings object. This parameter cannot be null. 301 | * 302 | * section: A pointer to a null-terminated C string. This parameter cannot 303 | * be null. 304 | * 305 | * Return value: The number of associations between keys and values in 306 | * the provided section. 307 | */ 308 | int settings_section_get_count(const Settings *settings, const char *section); 309 | 310 | /* 311 | * Enumerates all associations between keys and values in the provided 312 | * section. 313 | * 314 | * Parameters: 315 | * 316 | * settings: A pointer to a settings object. This parameter cannot be null. 317 | * 318 | * section: A pointer to a null-terminated C string. This parameter cannot 319 | * be null. 320 | * 321 | * enum_func: A pointer to a callback function that will be 322 | * called by this procedure once for every key associated 323 | * with a value. This parameter cannot be null. 324 | * 325 | * obj: A pointer to a client-specific object. This parameter will be 326 | * passed back to the client's callback function. This parameter can 327 | * be null. 328 | * 329 | * Return value: 1 if enumeration completed, 0 otherwise. 330 | */ 331 | int settings_section_enum(const Settings *settings, const char *section, settings_section_enum_func enum_func, const void *obj); 332 | 333 | #ifdef __cplusplus 334 | } 335 | #endif 336 | 337 | #endif 338 | 339 | /* 340 | 341 | GNU LESSER GENERAL PUBLIC LICENSE 342 | Version 3, 29 June 2007 343 | 344 | Copyright (C) 2007 Free Software Foundation, Inc. 345 | Everyone is permitted to copy and distribute verbatim copies 346 | of this license document, but changing it is not allowed. 347 | 348 | 349 | This version of the GNU Lesser General Public License incorporates 350 | the terms and conditions of version 3 of the GNU General Public 351 | License, supplemented by the additional permissions listed below. 352 | 353 | 0. Additional Definitions. 354 | 355 | As used herein, "this License" refers to version 3 of the GNU Lesser 356 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 357 | General Public License. 358 | 359 | "The Library" refers to a covered work governed by this License, 360 | other than an Application or a Combined Work as defined below. 361 | 362 | An "Application" is any work that makes use of an interface provided 363 | by the Library, but which is not otherwise based on the Library. 364 | Defining a subclass of a class defined by the Library is deemed a mode 365 | of using an interface provided by the Library. 366 | 367 | A "Combined Work" is a work produced by combining or linking an 368 | Application with the Library. The particular version of the Library 369 | with which the Combined Work was made is also called the "Linked 370 | Version". 371 | 372 | The "Minimal Corresponding Source" for a Combined Work means the 373 | Corresponding Source for the Combined Work, excluding any source code 374 | for portions of the Combined Work that, considered in isolation, are 375 | based on the Application, and not on the Linked Version. 376 | 377 | The "Corresponding Application Code" for a Combined Work means the 378 | object code and/or source code for the Application, including any data 379 | and utility programs needed for reproducing the Combined Work from the 380 | Application, but excluding the System Libraries of the Combined Work. 381 | 382 | 1. Exception to Section 3 of the GNU GPL. 383 | 384 | You may convey a covered work under sections 3 and 4 of this License 385 | without being bound by section 3 of the GNU GPL. 386 | 387 | 2. Conveying Modified Versions. 388 | 389 | If you modify a copy of the Library, and, in your modifications, a 390 | facility refers to a function or data to be supplied by an Application 391 | that uses the facility (other than as an argument passed when the 392 | facility is invoked), then you may convey a copy of the modified 393 | version: 394 | 395 | a) under this License, provided that you make a good faith effort to 396 | ensure that, in the event an Application does not supply the 397 | function or data, the facility still operates, and performs 398 | whatever part of its purpose remains meaningful, or 399 | 400 | b) under the GNU GPL, with none of the additional permissions of 401 | this License applicable to that copy. 402 | 403 | 3. Object Code Incorporating Material from Library Header Files. 404 | 405 | The object code form of an Application may incorporate material from 406 | a header file that is part of the Library. You may convey such object 407 | code under terms of your choice, provided that, if the incorporated 408 | material is not limited to numerical parameters, data structure 409 | layouts and accessors, or small macros, inline functions and templates 410 | (ten or fewer lines in length), you do both of the following: 411 | 412 | a) Give prominent notice with each copy of the object code that the 413 | Library is used in it and that the Library and its use are 414 | covered by this License. 415 | 416 | b) Accompany the object code with a copy of the GNU GPL and this license 417 | document. 418 | 419 | 4. Combined Works. 420 | 421 | You may convey a Combined Work under terms of your choice that, 422 | taken together, effectively do not restrict modification of the 423 | portions of the Library contained in the Combined Work and reverse 424 | engineering for debugging such modifications, if you also do each of 425 | the following: 426 | 427 | a) Give prominent notice with each copy of the Combined Work that 428 | the Library is used in it and that the Library and its use are 429 | covered by this License. 430 | 431 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 432 | document. 433 | 434 | c) For a Combined Work that displays copyright notices during 435 | execution, include the copyright notice for the Library among 436 | these notices, as well as a reference directing the user to the 437 | copies of the GNU GPL and this license document. 438 | 439 | d) Do one of the following: 440 | 441 | 0) Convey the Minimal Corresponding Source under the terms of this 442 | License, and the Corresponding Application Code in a form 443 | suitable for, and under terms that permit, the user to 444 | recombine or relink the Application with a modified version of 445 | the Linked Version to produce a modified Combined Work, in the 446 | manner specified by section 6 of the GNU GPL for conveying 447 | Corresponding Source. 448 | 449 | 1) Use a suitable shared library mechanism for linking with the 450 | Library. A suitable mechanism is one that (a) uses at run time 451 | a copy of the Library already present on the user's computer 452 | system, and (b) will operate properly with a modified version 453 | of the Library that is interface-compatible with the Linked 454 | Version. 455 | 456 | e) Provide Installation Information, but only if you would otherwise 457 | be required to provide such information under section 6 of the 458 | GNU GPL, and only to the extent that such information is 459 | necessary to install and execute a modified version of the 460 | Combined Work produced by recombining or relinking the 461 | Application with a modified version of the Linked Version. (If 462 | you use option 4d0, the Installation Information must accompany 463 | the Minimal Corresponding Source and Corresponding Application 464 | Code. If you use option 4d1, you must provide the Installation 465 | Information in the manner specified by section 6 of the GNU GPL 466 | for conveying Corresponding Source.) 467 | 468 | 5. Combined Libraries. 469 | 470 | You may place library facilities that are a work based on the 471 | Library side by side in a single library together with other library 472 | facilities that are not Applications and are not covered by this 473 | License, and convey such a combined library under terms of your 474 | choice, if you do both of the following: 475 | 476 | a) Accompany the combined library with a copy of the same work based 477 | on the Library, uncombined with any other library facilities, 478 | conveyed under the terms of this License. 479 | 480 | b) Give prominent notice with the combined library that part of it 481 | is a work based on the Library, and explaining where to find the 482 | accompanying uncombined form of the same work. 483 | 484 | 6. Revised Versions of the GNU Lesser General Public License. 485 | 486 | The Free Software Foundation may publish revised and/or new versions 487 | of the GNU Lesser General Public License from time to time. Such new 488 | versions will be similar in spirit to the present version, but may 489 | differ in detail to address new problems or concerns. 490 | 491 | Each version is given a distinguishing version number. If the 492 | Library as you received it specifies that a certain numbered version 493 | of the GNU Lesser General Public License "or any later version" 494 | applies to it, you have the option of following the terms and 495 | conditions either of that published version or of any later version 496 | published by the Free Software Foundation. If the Library as you 497 | received it does not specify a version number of the GNU Lesser 498 | General Public License, you may choose any version of the GNU Lesser 499 | General Public License ever published by the Free Software Foundation. 500 | 501 | If the Library as you received it specifies that a proxy can decide 502 | whether future versions of the GNU Lesser General Public License shall 503 | apply, that proxy's public statement of acceptance of any version is 504 | permanent authorization for you to choose that version for the 505 | Library. 506 | 507 | */ 508 | -------------------------------------------------------------------------------- /src/strmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * strmap version 2.0.0 3 | * 4 | * ANSI C hash table for strings. 5 | * 6 | * Version history: 7 | * 1.0.0 - initial release 8 | * 2.0.0 - changed function prefix from strmap to sm to ensure 9 | * ANSI C compatibility 10 | * 11 | * strmap.c 12 | * 13 | * Copyright (c) 2009, 2011 Per Ola Kristensson. 14 | * 15 | * Per Ola Kristensson 16 | * Inference Group, Department of Physics 17 | * University of Cambridge 18 | * Cavendish Laboratory 19 | * JJ Thomson Avenue 20 | * CB3 0HE Cambridge 21 | * United Kingdom 22 | * 23 | * strmap is free software: you can redistribute it and/or modify 24 | * it under the terms of the GNU Lesser General Public License as published by 25 | * the Free Software Foundation, either version 3 of the License, or 26 | * (at your option) any later version. 27 | * 28 | * strmap is distributed in the hope that it will be useful, 29 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | * GNU Lesser General Public License for more details. 32 | * 33 | * You should have received a copy of the GNU Lesser General Public License 34 | * along with strmap. If not, see . 35 | */ 36 | #include "strmap.h" 37 | 38 | typedef struct Pair Pair; 39 | 40 | typedef struct Bucket Bucket; 41 | 42 | struct Pair { 43 | char *key; 44 | char *value; 45 | }; 46 | 47 | struct Bucket { 48 | unsigned int count; 49 | Pair *pairs; 50 | }; 51 | 52 | struct StrMap { 53 | unsigned int count; 54 | Bucket *buckets; 55 | }; 56 | 57 | static Pair * get_pair(Bucket *bucket, const char *key); 58 | static unsigned long hash(const char *str); 59 | 60 | StrMap * sm_new(unsigned int capacity) 61 | { 62 | StrMap *map; 63 | 64 | map = (StrMap*)malloc(sizeof(StrMap)); 65 | 66 | if (map == NULL) { 67 | return NULL; 68 | } 69 | 70 | map->count = capacity; 71 | map->buckets = (Bucket*)malloc(map->count * sizeof(Bucket)); 72 | 73 | if (map->buckets == NULL) { 74 | free(map); 75 | return NULL; 76 | } 77 | 78 | memset(map->buckets, 0, map->count * sizeof(Bucket)); 79 | return map; 80 | } 81 | 82 | void sm_delete(StrMap *map) 83 | { 84 | unsigned int i, j, n, m; 85 | Bucket *bucket; 86 | Pair *pair; 87 | 88 | if (map == NULL) { 89 | return; 90 | } 91 | 92 | n = map->count; 93 | bucket = map->buckets; 94 | i = 0; 95 | 96 | while (i < n) { 97 | m = bucket->count; 98 | pair = bucket->pairs; 99 | j = 0; 100 | 101 | while(j < m) { 102 | free(pair->key); 103 | free(pair->value); 104 | pair++; 105 | j++; 106 | } 107 | 108 | free(bucket->pairs); 109 | bucket++; 110 | i++; 111 | } 112 | 113 | free(map->buckets); 114 | free(map); 115 | } 116 | 117 | int sm_get(const StrMap *map, const char *key, char *out_buf, unsigned int n_out_buf) 118 | { 119 | unsigned int index; 120 | Bucket *bucket; 121 | Pair *pair; 122 | 123 | if (map == NULL) { 124 | return 0; 125 | } 126 | 127 | if (key == NULL) { 128 | return 0; 129 | } 130 | 131 | index = hash(key) % map->count; 132 | bucket = &(map->buckets[index]); 133 | pair = get_pair(bucket, key); 134 | 135 | if (pair == NULL) { 136 | return 0; 137 | } 138 | 139 | if (out_buf == NULL && n_out_buf == 0) { 140 | return strlen(pair->value) + 1; 141 | } 142 | 143 | if (out_buf == NULL) { 144 | return 0; 145 | } 146 | 147 | if (strlen(pair->value) >= n_out_buf) { 148 | return 0; 149 | } 150 | 151 | strcpy(out_buf, pair->value); 152 | return 1; 153 | } 154 | 155 | int sm_exists(const StrMap *map, const char *key) 156 | { 157 | unsigned int index; 158 | Bucket *bucket; 159 | Pair *pair; 160 | 161 | if (map == NULL) { 162 | return 0; 163 | } 164 | 165 | if (key == NULL) { 166 | return 0; 167 | } 168 | 169 | index = hash(key) % map->count; 170 | bucket = &(map->buckets[index]); 171 | pair = get_pair(bucket, key); 172 | 173 | if (pair == NULL) { 174 | return 0; 175 | } 176 | 177 | return 1; 178 | } 179 | 180 | int sm_put(StrMap *map, const char *key, const char *value) 181 | { 182 | unsigned int key_len, value_len, index; 183 | Bucket *bucket; 184 | Pair *tmp_pairs, *pair; 185 | char *tmp_value; 186 | char *new_key, *new_value; 187 | 188 | if (map == NULL) { 189 | return 0; 190 | } 191 | 192 | if (key == NULL || value == NULL) { 193 | return 0; 194 | } 195 | 196 | key_len = strlen(key); 197 | value_len = strlen(value); 198 | /* Get a pointer to the bucket the key string hashes to */ 199 | index = hash(key) % map->count; 200 | bucket = &(map->buckets[index]); 201 | 202 | /* Check if we can handle insertion by simply replacing 203 | * an existing value in a key-value pair in the bucket. 204 | */ 205 | if ((pair = get_pair(bucket, key)) != NULL) { 206 | /* The bucket contains a pair that matches the provided key, 207 | * change the value for that pair to the new value. 208 | */ 209 | if (strlen(pair->value) < value_len) { 210 | /* If the new value is larger than the old value, re-allocate 211 | * space for the new larger value. 212 | */ 213 | tmp_value = (char*)realloc(pair->value, (value_len + 1) * sizeof(char)); 214 | 215 | if (tmp_value == NULL) { 216 | return 0; 217 | } 218 | 219 | pair->value = tmp_value; 220 | } 221 | 222 | /* Copy the new value into the pair that matches the key */ 223 | strcpy(pair->value, value); 224 | return 1; 225 | } 226 | 227 | /* Allocate space for a new key and value */ 228 | new_key = (char*)malloc((key_len + 1) * sizeof(char)); 229 | 230 | if (new_key == NULL) { 231 | return 0; 232 | } 233 | 234 | new_value = (char*)malloc((value_len + 1) * sizeof(char)); 235 | 236 | if (new_value == NULL) { 237 | free(new_key); 238 | return 0; 239 | } 240 | 241 | /* Create a key-value pair */ 242 | if (bucket->count == 0) { 243 | /* The bucket is empty, lazily allocate space for a single 244 | * key-value pair. 245 | */ 246 | bucket->pairs = (Pair*)malloc(sizeof(Pair)); 247 | 248 | if (bucket->pairs == NULL) { 249 | free(new_key); 250 | free(new_value); 251 | return 0; 252 | } 253 | 254 | bucket->count = 1; 255 | 256 | } else { 257 | /* The bucket wasn't empty but no pair existed that matches the provided 258 | * key, so create a new key-value pair. 259 | */ 260 | tmp_pairs = (Pair*)realloc(bucket->pairs, (bucket->count + 1) * sizeof(Pair)); 261 | 262 | if (tmp_pairs == NULL) { 263 | free(new_key); 264 | free(new_value); 265 | return 0; 266 | } 267 | 268 | bucket->pairs = tmp_pairs; 269 | bucket->count++; 270 | } 271 | 272 | /* Get the last pair in the chain for the bucket */ 273 | pair = &(bucket->pairs[bucket->count - 1]); 274 | pair->key = new_key; 275 | pair->value = new_value; 276 | /* Copy the key and its value into the key-value pair */ 277 | strcpy(pair->key, key); 278 | strcpy(pair->value, value); 279 | return 1; 280 | } 281 | 282 | int sm_get_count(const StrMap *map) 283 | { 284 | unsigned int i, j, n, m; 285 | unsigned int count; 286 | Bucket *bucket; 287 | Pair *pair; 288 | 289 | if (map == NULL) { 290 | return 0; 291 | } 292 | 293 | bucket = map->buckets; 294 | n = map->count; 295 | i = 0; 296 | count = 0; 297 | 298 | while (i < n) { 299 | pair = bucket->pairs; 300 | m = bucket->count; 301 | j = 0; 302 | 303 | while (j < m) { 304 | count++; 305 | pair++; 306 | j++; 307 | } 308 | 309 | bucket++; 310 | i++; 311 | } 312 | 313 | return count; 314 | } 315 | 316 | int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj) 317 | { 318 | unsigned int i, j, n, m; 319 | Bucket *bucket; 320 | Pair *pair; 321 | 322 | if (map == NULL) { 323 | return 0; 324 | } 325 | 326 | if (enum_func == NULL) { 327 | return 0; 328 | } 329 | 330 | bucket = map->buckets; 331 | n = map->count; 332 | i = 0; 333 | 334 | while (i < n) { 335 | pair = bucket->pairs; 336 | m = bucket->count; 337 | j = 0; 338 | 339 | while (j < m) { 340 | enum_func(pair->key, pair->value, obj); 341 | pair++; 342 | j++; 343 | } 344 | 345 | bucket++; 346 | i++; 347 | } 348 | 349 | return 1; 350 | } 351 | 352 | /* 353 | * Returns a pair from the bucket that matches the provided key, 354 | * or null if no such pair exist. 355 | */ 356 | static Pair * get_pair(Bucket *bucket, const char *key) 357 | { 358 | unsigned int i, n; 359 | Pair *pair; 360 | 361 | n = bucket->count; 362 | 363 | if (n == 0) { 364 | return NULL; 365 | } 366 | 367 | pair = bucket->pairs; 368 | i = 0; 369 | 370 | while (i < n) { 371 | if (pair->key != NULL && pair->value != NULL) { 372 | if (strcmp(pair->key, key) == 0) { 373 | return pair; 374 | } 375 | } 376 | 377 | pair++; 378 | i++; 379 | } 380 | 381 | return NULL; 382 | } 383 | 384 | /* 385 | * Returns a hash code for the provided string. 386 | */ 387 | static unsigned long hash(const char *str) 388 | { 389 | unsigned long hash = 5381; 390 | int c; 391 | 392 | while ((c = *str++)) { 393 | hash = ((hash << 5) + hash) + c; 394 | } 395 | 396 | return hash; 397 | } 398 | 399 | /* 400 | 401 | GNU LESSER GENERAL PUBLIC LICENSE 402 | Version 3, 29 June 2007 403 | 404 | Copyright (C) 2007 Free Software Foundation, Inc. 405 | Everyone is permitted to copy and distribute verbatim copies 406 | of this license document, but changing it is not allowed. 407 | 408 | 409 | This version of the GNU Lesser General Public License incorporates 410 | the terms and conditions of version 3 of the GNU General Public 411 | License, supplemented by the additional permissions listed below. 412 | 413 | 0. Additional Definitions. 414 | 415 | As used herein, "this License" refers to version 3 of the GNU Lesser 416 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 417 | General Public License. 418 | 419 | "The Library" refers to a covered work governed by this License, 420 | other than an Application or a Combined Work as defined below. 421 | 422 | An "Application" is any work that makes use of an interface provided 423 | by the Library, but which is not otherwise based on the Library. 424 | Defining a subclass of a class defined by the Library is deemed a mode 425 | of using an interface provided by the Library. 426 | 427 | A "Combined Work" is a work produced by combining or linking an 428 | Application with the Library. The particular version of the Library 429 | with which the Combined Work was made is also called the "Linked 430 | Version". 431 | 432 | The "Minimal Corresponding Source" for a Combined Work means the 433 | Corresponding Source for the Combined Work, excluding any source code 434 | for portions of the Combined Work that, considered in isolation, are 435 | based on the Application, and not on the Linked Version. 436 | 437 | The "Corresponding Application Code" for a Combined Work means the 438 | object code and/or source code for the Application, including any data 439 | and utility programs needed for reproducing the Combined Work from the 440 | Application, but excluding the System Libraries of the Combined Work. 441 | 442 | 1. Exception to Section 3 of the GNU GPL. 443 | 444 | You may convey a covered work under sections 3 and 4 of this License 445 | without being bound by section 3 of the GNU GPL. 446 | 447 | 2. Conveying Modified Versions. 448 | 449 | If you modify a copy of the Library, and, in your modifications, a 450 | facility refers to a function or data to be supplied by an Application 451 | that uses the facility (other than as an argument passed when the 452 | facility is invoked), then you may convey a copy of the modified 453 | version: 454 | 455 | a) under this License, provided that you make a good faith effort to 456 | ensure that, in the event an Application does not supply the 457 | function or data, the facility still operates, and performs 458 | whatever part of its purpose remains meaningful, or 459 | 460 | b) under the GNU GPL, with none of the additional permissions of 461 | this License applicable to that copy. 462 | 463 | 3. Object Code Incorporating Material from Library Header Files. 464 | 465 | The object code form of an Application may incorporate material from 466 | a header file that is part of the Library. You may convey such object 467 | code under terms of your choice, provided that, if the incorporated 468 | material is not limited to numerical parameters, data structure 469 | layouts and accessors, or small macros, inline functions and templates 470 | (ten or fewer lines in length), you do both of the following: 471 | 472 | a) Give prominent notice with each copy of the object code that the 473 | Library is used in it and that the Library and its use are 474 | covered by this License. 475 | 476 | b) Accompany the object code with a copy of the GNU GPL and this license 477 | document. 478 | 479 | 4. Combined Works. 480 | 481 | You may convey a Combined Work under terms of your choice that, 482 | taken together, effectively do not restrict modification of the 483 | portions of the Library contained in the Combined Work and reverse 484 | engineering for debugging such modifications, if you also do each of 485 | the following: 486 | 487 | a) Give prominent notice with each copy of the Combined Work that 488 | the Library is used in it and that the Library and its use are 489 | covered by this License. 490 | 491 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 492 | document. 493 | 494 | c) For a Combined Work that displays copyright notices during 495 | execution, include the copyright notice for the Library among 496 | these notices, as well as a reference directing the user to the 497 | copies of the GNU GPL and this license document. 498 | 499 | d) Do one of the following: 500 | 501 | 0) Convey the Minimal Corresponding Source under the terms of this 502 | License, and the Corresponding Application Code in a form 503 | suitable for, and under terms that permit, the user to 504 | recombine or relink the Application with a modified version of 505 | the Linked Version to produce a modified Combined Work, in the 506 | manner specified by section 6 of the GNU GPL for conveying 507 | Corresponding Source. 508 | 509 | 1) Use a suitable shared library mechanism for linking with the 510 | Library. A suitable mechanism is one that (a) uses at run time 511 | a copy of the Library already present on the user's computer 512 | system, and (b) will operate properly with a modified version 513 | of the Library that is interface-compatible with the Linked 514 | Version. 515 | 516 | e) Provide Installation Information, but only if you would otherwise 517 | be required to provide such information under section 6 of the 518 | GNU GPL, and only to the extent that such information is 519 | necessary to install and execute a modified version of the 520 | Combined Work produced by recombining or relinking the 521 | Application with a modified version of the Linked Version. (If 522 | you use option 4d0, the Installation Information must accompany 523 | the Minimal Corresponding Source and Corresponding Application 524 | Code. If you use option 4d1, you must provide the Installation 525 | Information in the manner specified by section 6 of the GNU GPL 526 | for conveying Corresponding Source.) 527 | 528 | 5. Combined Libraries. 529 | 530 | You may place library facilities that are a work based on the 531 | Library side by side in a single library together with other library 532 | facilities that are not Applications and are not covered by this 533 | License, and convey such a combined library under terms of your 534 | choice, if you do both of the following: 535 | 536 | a) Accompany the combined library with a copy of the same work based 537 | on the Library, uncombined with any other library facilities, 538 | conveyed under the terms of this License. 539 | 540 | b) Give prominent notice with the combined library that part of it 541 | is a work based on the Library, and explaining where to find the 542 | accompanying uncombined form of the same work. 543 | 544 | 6. Revised Versions of the GNU Lesser General Public License. 545 | 546 | The Free Software Foundation may publish revised and/or new versions 547 | of the GNU Lesser General Public License from time to time. Such new 548 | versions will be similar in spirit to the present version, but may 549 | differ in detail to address new problems or concerns. 550 | 551 | Each version is given a distinguishing version number. If the 552 | Library as you received it specifies that a certain numbered version 553 | of the GNU Lesser General Public License "or any later version" 554 | applies to it, you have the option of following the terms and 555 | conditions either of that published version or of any later version 556 | published by the Free Software Foundation. If the Library as you 557 | received it does not specify a version number of the GNU Lesser 558 | General Public License, you may choose any version of the GNU Lesser 559 | General Public License ever published by the Free Software Foundation. 560 | 561 | If the Library as you received it specifies that a proxy can decide 562 | whether future versions of the GNU Lesser General Public License shall 563 | apply, that proxy's public statement of acceptance of any version is 564 | permanent authorization for you to choose that version for the 565 | Library. 566 | 567 | */ 568 | -------------------------------------------------------------------------------- /src/strmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * strmap version 2.0.0 3 | * 4 | * ANSI C hash table for strings. 5 | * 6 | * Version history: 7 | * 1.0.0 - initial release 8 | * 2.0.0 - changed function prefix from strmap to sm to ensure 9 | * ANSI C compatibility 10 | * 11 | * strmap.h 12 | * 13 | * Copyright (c) 2009, 2011 Per Ola Kristensson. 14 | * 15 | * Per Ola Kristensson 16 | * Inference Group, Department of Physics 17 | * University of Cambridge 18 | * Cavendish Laboratory 19 | * JJ Thomson Avenue 20 | * CB3 0HE Cambridge 21 | * United Kingdom 22 | * 23 | * strmap is free software: you can redistribute it and/or modify 24 | * it under the terms of the GNU Lesser General Public License as published by 25 | * the Free Software Foundation, either version 3 of the License, or 26 | * (at your option) any later version. 27 | * 28 | * strmap is distributed in the hope that it will be useful, 29 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | * GNU Lesser General Public License for more details. 32 | * 33 | * You should have received a copy of the GNU Lesser General Public License 34 | * along with strmap. If not, see . 35 | */ 36 | #ifndef _STRMAP_H_ 37 | #define _STRMAP_H_ 38 | 39 | #ifdef __cplusplus 40 | extern "C" 41 | { 42 | #endif 43 | 44 | #include 45 | #include 46 | 47 | typedef struct StrMap StrMap; 48 | 49 | /* 50 | * This callback function is called once per key-value when enumerating 51 | * all keys associated to values. 52 | * 53 | * Parameters: 54 | * 55 | * key: A pointer to a null-terminated C string. The string must not 56 | * be modified by the client. 57 | * 58 | * value: A pointer to a null-terminated C string. The string must 59 | * not be modified by the client. 60 | * 61 | * obj: A pointer to a client-specific object. This parameter may be 62 | * null. 63 | * 64 | * Return value: None. 65 | */ 66 | typedef void(*sm_enum_func)(const char *key, const char *value, const void *obj); 67 | 68 | /* 69 | * Creates a string map. 70 | * 71 | * Parameters: 72 | * 73 | * capacity: The number of top-level slots this string map 74 | * should allocate. This parameter must be > 0. 75 | * 76 | * Return value: A pointer to a string map object, 77 | * or null if a new string map could not be allocated. 78 | */ 79 | StrMap * sm_new(unsigned int capacity); 80 | 81 | /* 82 | * Releases all memory held by a string map object. 83 | * 84 | * Parameters: 85 | * 86 | * map: A pointer to a string map. This parameter cannot be null. 87 | * If the supplied string map has been previously released, the 88 | * behaviour of this function is undefined. 89 | * 90 | * Return value: None. 91 | */ 92 | void sm_delete(StrMap *map); 93 | 94 | /* 95 | * Returns the value associated with the supplied key. 96 | * 97 | * Parameters: 98 | * 99 | * map: A pointer to a string map. This parameter cannot be null. 100 | * 101 | * key: A pointer to a null-terminated C string. This parameter cannot 102 | * be null. 103 | * 104 | * out_buf: A pointer to an output buffer which will contain the value, 105 | * if it exists and fits into the buffer. 106 | * 107 | * n_out_buf: The size of the output buffer in bytes. 108 | * 109 | * Return value: If out_buf is set to null and n_out_buf is set to 0 the return 110 | * value will be the number of bytes required to store the value (if it exists) 111 | * and its null-terminator. For all other parameter configurations the return value 112 | * is 1 if an associated value was found and completely copied into the output buffer, 113 | * 0 otherwise. 114 | */ 115 | int sm_get(const StrMap *map, const char *key, char *out_buf, unsigned int n_out_buf); 116 | 117 | /* 118 | * Queries the existence of a key. 119 | * 120 | * Parameters: 121 | * 122 | * map: A pointer to a string map. This parameter cannot be null. 123 | * 124 | * key: A pointer to a null-terminated C string. This parameter cannot 125 | * be null. 126 | * 127 | * Return value: 1 if the key exists, 0 otherwise. 128 | */ 129 | int sm_exists(const StrMap *map, const char *key); 130 | 131 | /* 132 | * Associates a value with the supplied key. If the key is already 133 | * associated with a value, the previous value is replaced. 134 | * 135 | * Parameters: 136 | * 137 | * map: A pointer to a string map. This parameter cannot be null. 138 | * 139 | * key: A pointer to a null-terminated C string. This parameter 140 | * cannot be null. The string must have a string length > 0. The 141 | * string will be copied. 142 | * 143 | * value: A pointer to a null-terminated C string. This parameter 144 | * cannot be null. The string must have a string length > 0. The 145 | * string will be copied. 146 | * 147 | * Return value: 1 if the association succeeded, 0 otherwise. 148 | */ 149 | int sm_put(StrMap *map, const char *key, const char *value); 150 | 151 | /* 152 | * Returns the number of associations between keys and values. 153 | * 154 | * Parameters: 155 | * 156 | * map: A pointer to a string map. This parameter cannot be null. 157 | * 158 | * Return value: The number of associations between keys and values. 159 | */ 160 | int sm_get_count(const StrMap *map); 161 | 162 | /* 163 | * Enumerates all associations between keys and values. 164 | * 165 | * Parameters: 166 | * 167 | * map: A pointer to a string map. This parameter cannot be null. 168 | * 169 | * enum_func: A pointer to a callback function that will be 170 | * called by this procedure once for every key associated 171 | * with a value. This parameter cannot be null. 172 | * 173 | * obj: A pointer to a client-specific object. This parameter will be 174 | * passed back to the client's callback function. This parameter can 175 | * be null. 176 | * 177 | * Return value: 1 if enumeration completed, 0 otherwise. 178 | */ 179 | int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj); 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | #endif 186 | 187 | /* 188 | 189 | GNU LESSER GENERAL PUBLIC LICENSE 190 | Version 3, 29 June 2007 191 | 192 | Copyright (C) 2007 Free Software Foundation, Inc. 193 | Everyone is permitted to copy and distribute verbatim copies 194 | of this license document, but changing it is not allowed. 195 | 196 | 197 | This version of the GNU Lesser General Public License incorporates 198 | the terms and conditions of version 3 of the GNU General Public 199 | License, supplemented by the additional permissions listed below. 200 | 201 | 0. Additional Definitions. 202 | 203 | As used herein, "this License" refers to version 3 of the GNU Lesser 204 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 205 | General Public License. 206 | 207 | "The Library" refers to a covered work governed by this License, 208 | other than an Application or a Combined Work as defined below. 209 | 210 | An "Application" is any work that makes use of an interface provided 211 | by the Library, but which is not otherwise based on the Library. 212 | Defining a subclass of a class defined by the Library is deemed a mode 213 | of using an interface provided by the Library. 214 | 215 | A "Combined Work" is a work produced by combining or linking an 216 | Application with the Library. The particular version of the Library 217 | with which the Combined Work was made is also called the "Linked 218 | Version". 219 | 220 | The "Minimal Corresponding Source" for a Combined Work means the 221 | Corresponding Source for the Combined Work, excluding any source code 222 | for portions of the Combined Work that, considered in isolation, are 223 | based on the Application, and not on the Linked Version. 224 | 225 | The "Corresponding Application Code" for a Combined Work means the 226 | object code and/or source code for the Application, including any data 227 | and utility programs needed for reproducing the Combined Work from the 228 | Application, but excluding the System Libraries of the Combined Work. 229 | 230 | 1. Exception to Section 3 of the GNU GPL. 231 | 232 | You may convey a covered work under sections 3 and 4 of this License 233 | without being bound by section 3 of the GNU GPL. 234 | 235 | 2. Conveying Modified Versions. 236 | 237 | If you modify a copy of the Library, and, in your modifications, a 238 | facility refers to a function or data to be supplied by an Application 239 | that uses the facility (other than as an argument passed when the 240 | facility is invoked), then you may convey a copy of the modified 241 | version: 242 | 243 | a) under this License, provided that you make a good faith effort to 244 | ensure that, in the event an Application does not supply the 245 | function or data, the facility still operates, and performs 246 | whatever part of its purpose remains meaningful, or 247 | 248 | b) under the GNU GPL, with none of the additional permissions of 249 | this License applicable to that copy. 250 | 251 | 3. Object Code Incorporating Material from Library Header Files. 252 | 253 | The object code form of an Application may incorporate material from 254 | a header file that is part of the Library. You may convey such object 255 | code under terms of your choice, provided that, if the incorporated 256 | material is not limited to numerical parameters, data structure 257 | layouts and accessors, or small macros, inline functions and templates 258 | (ten or fewer lines in length), you do both of the following: 259 | 260 | a) Give prominent notice with each copy of the object code that the 261 | Library is used in it and that the Library and its use are 262 | covered by this License. 263 | 264 | b) Accompany the object code with a copy of the GNU GPL and this license 265 | document. 266 | 267 | 4. Combined Works. 268 | 269 | You may convey a Combined Work under terms of your choice that, 270 | taken together, effectively do not restrict modification of the 271 | portions of the Library contained in the Combined Work and reverse 272 | engineering for debugging such modifications, if you also do each of 273 | the following: 274 | 275 | a) Give prominent notice with each copy of the Combined Work that 276 | the Library is used in it and that the Library and its use are 277 | covered by this License. 278 | 279 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 280 | document. 281 | 282 | c) For a Combined Work that displays copyright notices during 283 | execution, include the copyright notice for the Library among 284 | these notices, as well as a reference directing the user to the 285 | copies of the GNU GPL and this license document. 286 | 287 | d) Do one of the following: 288 | 289 | 0) Convey the Minimal Corresponding Source under the terms of this 290 | License, and the Corresponding Application Code in a form 291 | suitable for, and under terms that permit, the user to 292 | recombine or relink the Application with a modified version of 293 | the Linked Version to produce a modified Combined Work, in the 294 | manner specified by section 6 of the GNU GPL for conveying 295 | Corresponding Source. 296 | 297 | 1) Use a suitable shared library mechanism for linking with the 298 | Library. A suitable mechanism is one that (a) uses at run time 299 | a copy of the Library already present on the user's computer 300 | system, and (b) will operate properly with a modified version 301 | of the Library that is interface-compatible with the Linked 302 | Version. 303 | 304 | e) Provide Installation Information, but only if you would otherwise 305 | be required to provide such information under section 6 of the 306 | GNU GPL, and only to the extent that such information is 307 | necessary to install and execute a modified version of the 308 | Combined Work produced by recombining or relinking the 309 | Application with a modified version of the Linked Version. (If 310 | you use option 4d0, the Installation Information must accompany 311 | the Minimal Corresponding Source and Corresponding Application 312 | Code. If you use option 4d1, you must provide the Installation 313 | Information in the manner specified by section 6 of the GNU GPL 314 | for conveying Corresponding Source.) 315 | 316 | 5. Combined Libraries. 317 | 318 | You may place library facilities that are a work based on the 319 | Library side by side in a single library together with other library 320 | facilities that are not Applications and are not covered by this 321 | License, and convey such a combined library under terms of your 322 | choice, if you do both of the following: 323 | 324 | a) Accompany the combined library with a copy of the same work based 325 | on the Library, uncombined with any other library facilities, 326 | conveyed under the terms of this License. 327 | 328 | b) Give prominent notice with the combined library that part of it 329 | is a work based on the Library, and explaining where to find the 330 | accompanying uncombined form of the same work. 331 | 332 | 6. Revised Versions of the GNU Lesser General Public License. 333 | 334 | The Free Software Foundation may publish revised and/or new versions 335 | of the GNU Lesser General Public License from time to time. Such new 336 | versions will be similar in spirit to the present version, but may 337 | differ in detail to address new problems or concerns. 338 | 339 | Each version is given a distinguishing version number. If the 340 | Library as you received it specifies that a certain numbered version 341 | of the GNU Lesser General Public License "or any later version" 342 | applies to it, you have the option of following the terms and 343 | conditions either of that published version or of any later version 344 | published by the Free Software Foundation. If the Library as you 345 | received it does not specify a version number of the GNU Lesser 346 | General Public License, you may choose any version of the GNU Lesser 347 | General Public License ever published by the Free Software Foundation. 348 | 349 | If the Library as you received it specifies that a proxy can decide 350 | whether future versions of the GNU Lesser General Public License shall 351 | apply, that proxy's public statement of acceptance of any version is 352 | permanent authorization for you to choose that version for the 353 | Library. 354 | 355 | */ 356 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define __USE_MISC 4 | #include 5 | 6 | #include "global.h" 7 | 8 | void mbp_log(int level, const char *fmt, ...) 9 | { 10 | va_list args; 11 | if (daemonize) { 12 | va_start(args, fmt); 13 | vsyslog(level, fmt, args); 14 | va_end(args); 15 | } 16 | 17 | va_start(args, fmt); 18 | vprintf(fmt, args); 19 | puts(""); // trailing newline 20 | va_end(args); 21 | } 22 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H_ 2 | #define _UTIL_H_ 3 | 4 | void mbp_log(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tests/main.c: -------------------------------------------------------------------------------- 1 | #include "minunit.h" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | (void)argc; 6 | tests(argv[0]); 7 | } 8 | -------------------------------------------------------------------------------- /tests/minunit.c: -------------------------------------------------------------------------------- 1 | /* file minunit_example.c */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../src/global.h" 10 | #include "../src/mbpfan.h" 11 | #include "../src/settings.h" 12 | #include "minunit.h" 13 | 14 | int tests_run = 0; 15 | 16 | static void free_fans(t_fans* fans) 17 | { 18 | while (fans != NULL) { 19 | t_fans* tmp = fans->next; 20 | free(fans->fan_manual_path); 21 | free(fans->fan_output_path); 22 | free(fans->label); 23 | free(fans); 24 | fans = tmp; 25 | } 26 | } 27 | 28 | static void free_sensors(t_sensors* sensors) 29 | { 30 | while (sensors != NULL) { 31 | t_sensors* tmp = sensors->next; 32 | free(sensors->path); 33 | free(sensors); 34 | sensors = tmp; 35 | } 36 | } 37 | 38 | static const char *test_sensor_paths() 39 | { 40 | t_sensors* sensors = retrieve_sensors(); 41 | mu_assert("No sensors found", sensors != NULL); 42 | t_sensors* tmp = sensors; 43 | 44 | while(tmp != NULL) { 45 | mu_assert("Sensor does not have a valid path", tmp->path != NULL); 46 | 47 | if(tmp->path != NULL) { 48 | mu_assert("Sensor does not have valid temperature", tmp->temperature > 0); 49 | } 50 | 51 | tmp = tmp->next; 52 | } 53 | 54 | free_sensors(sensors); 55 | return 0; 56 | } 57 | 58 | 59 | static const char *test_fan_paths() 60 | { 61 | t_fans* fans = retrieve_fans(); 62 | mu_assert("No fans found", fans != NULL); 63 | t_fans* tmp = fans; 64 | int found_fan_path = 0; 65 | 66 | while(tmp != NULL) { 67 | if(tmp->fan_output_path != NULL) { 68 | found_fan_path++; 69 | } 70 | 71 | tmp = tmp->next; 72 | } 73 | 74 | mu_assert("No fans found", found_fan_path != 0); 75 | free_fans(fans); 76 | return 0; 77 | } 78 | 79 | unsigned time_seed() 80 | { 81 | time_t now = time ( 0 ); 82 | unsigned char *p = (unsigned char *)&now; 83 | unsigned seed = 0; 84 | size_t i; 85 | 86 | for ( i = 0; i < sizeof now; i++ ) { 87 | seed = seed * ( UCHAR_MAX + 2U ) + p[i]; 88 | } 89 | 90 | return seed; 91 | } 92 | 93 | // nothing better than a horrible piece of code to 94 | // stress a little bit the CPU 95 | int stress(int n) 96 | { 97 | int f = n; 98 | 99 | while (f > 0) { 100 | while(n > 0) { 101 | srand ( time_seed() ); 102 | n--; 103 | } 104 | 105 | f--; 106 | n = f; 107 | } 108 | return 0; 109 | } 110 | 111 | static const char *test_get_temp() 112 | { 113 | t_sensors* sensors = retrieve_sensors(); 114 | mu_assert("No sensors found", sensors != NULL); 115 | unsigned short temp_1 = get_temp(sensors); 116 | mu_assert("Invalid Global Temperature Found", temp_1 > 1 && temp_1 < 150); 117 | stress(2000); 118 | unsigned short temp_2 = get_temp(sensors); 119 | mu_assert("Invalid Higher temp test (if fan was already spinning high, this is not worrying)", temp_1 < temp_2); 120 | free_sensors(sensors); 121 | return 0; 122 | } 123 | 124 | static const char *test_config_file() 125 | { 126 | FILE *f = NULL; 127 | Settings *settings = NULL; 128 | f = fopen("mbpfan.conf", "r"); 129 | mu_assert("No config file found", f != NULL); 130 | 131 | if (f == NULL) { 132 | return 0; 133 | } 134 | 135 | settings = settings_open(f); 136 | fclose(f); 137 | mu_assert("Could not read settings from config file", settings != NULL); 138 | 139 | if (settings == NULL) { 140 | return 0; 141 | } 142 | 143 | mu_assert("Could not read low_temp from config file",settings_get_int(settings, "general", "low_temp") != 0); 144 | mu_assert("Could not read high_temp from config file",settings_get_int(settings, "general", "high_temp") != 0); 145 | mu_assert("Could not read max_temp from config file",settings_get_int(settings, "general", "max_temp") != 0); 146 | mu_assert("Could not read polling_interval from config file",settings_get_int(settings, "general", "polling_interval") != 0); 147 | /* Destroy the settings object */ 148 | settings_delete(settings); 149 | 150 | return 0; 151 | } 152 | 153 | static const char *test_settings() 154 | { 155 | t_fans* fan = (t_fans *) malloc( sizeof( t_fans ) ); 156 | fan->fan_id = 1; 157 | fan->fan_max_speed = -1; 158 | fan->next = NULL; 159 | 160 | retrieve_settings("./mbpfan.conf.test1", fan); 161 | // choosing the maximum for iMac mid 2011 162 | mu_assert("max_fan_speed value is not 2600", fan->fan_max_speed == 2600); 163 | mu_assert("polling_interval is not 2", polling_interval == 2); 164 | 165 | fan->fan_min_speed = -1; 166 | retrieve_settings("./mbpfan.conf.test0", fan); 167 | mu_assert("min_fan_speed value is not 2000", fan->fan_min_speed == 2000); 168 | mu_assert("polling_interval is not 7", polling_interval == 7); 169 | 170 | t_fans* fan2 = (t_fans *)malloc(sizeof(t_fans)); 171 | fan2->fan_id = 2; 172 | fan2->fan_max_speed = -1; 173 | fan2->next = NULL; 174 | fan->next = fan2; 175 | 176 | retrieve_settings("./mbpfan.conf.test2", fan); 177 | mu_assert("min_fan1_speed value is not 2000", fan->fan_min_speed == 2000); 178 | mu_assert("min_fan2_speed value is not 2000", fan->next->fan_min_speed == 2000); 179 | 180 | free(fan2); 181 | fan->next = NULL; 182 | free(fan); 183 | 184 | return 0; 185 | 186 | } 187 | int received = 0; 188 | 189 | static void handler(int signal) 190 | { 191 | t_fans* fan = (t_fans *) malloc( sizeof( t_fans ) ); 192 | fan->fan_id = 1; 193 | fan->next = NULL; 194 | 195 | 196 | switch(signal) { 197 | case SIGHUP: 198 | received = 1; 199 | retrieve_settings("./mbpfan.conf.test1", fan); 200 | free(fan); 201 | break; 202 | 203 | default: 204 | received = 0; 205 | free(fan); 206 | break; 207 | } 208 | } 209 | 210 | static const char *test_sighup_receive() 211 | { 212 | signal(SIGHUP, handler); 213 | raise(SIGHUP); 214 | mu_assert("did not receive SIGHUP signal", received == 1); 215 | return 0; 216 | } 217 | 218 | static const char *test_settings_reload() 219 | { 220 | t_fans* fan = (t_fans *) malloc( sizeof( t_fans ) ); 221 | fan->fan_id = 1; 222 | fan->fan_min_speed = -1; 223 | fan->fan_manual_path = NULL; 224 | fan->fan_output_path = NULL; 225 | fan->label = NULL; 226 | fan->next = NULL; 227 | 228 | signal(SIGHUP, handler); 229 | retrieve_settings("./mbpfan.conf", fan); 230 | printf("Testing the _supplied_ mbpfan.conf (not the one you are using)..\n"); 231 | // cannot tests min_fan_speed since it is not set and thus auto-detected 232 | mu_assert("polling_interval is not 1 before SIGHUP", polling_interval == 1); 233 | raise(SIGHUP); 234 | // cannot tests min_fan_speed since it is not set and thus auto-detected 235 | mu_assert("polling_interval is not 2 after SIGHUP", polling_interval == 2); 236 | retrieve_settings("./mbpfan.conf", fan); 237 | free_fans(fan); 238 | return 0; 239 | } 240 | 241 | 242 | static const char *all_tests() 243 | { 244 | mu_run_test(test_sensor_paths); 245 | mu_run_test(test_fan_paths); 246 | mu_run_test(test_get_temp); 247 | mu_run_test(test_config_file); 248 | mu_run_test(test_settings); 249 | mu_run_test(test_sighup_receive); 250 | mu_run_test(test_settings_reload); 251 | return 0; 252 | } 253 | 254 | int tests(const char *program_path) 255 | { 256 | verbose = 1; 257 | 258 | check_requirements(program_path); 259 | 260 | printf("Starting the tests..\n"); 261 | printf("It is normal for them to take a bit to finish.\n"); 262 | 263 | const char *result = all_tests(); 264 | 265 | if (result != 0) { 266 | printf("Error: %s \n", result); 267 | 268 | } else { 269 | printf("ALL TESTS PASSED\n"); 270 | } 271 | 272 | printf("Tests run: %d\n", tests_run); 273 | 274 | return result != 0; 275 | } 276 | -------------------------------------------------------------------------------- /tests/minunit.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the MinUnit testing framework - http://www.jera.com/techinfo/jtns/jtn002.html 3 | */ 4 | 5 | #ifndef _MINUNIT_H_ 6 | #define _MINUNIT_H_ 7 | 8 | #define mu_assert(message, test) do { if (!(test)) return message; } while (0) 9 | #define mu_run_test(test) do { const char *message = test(); tests_run++; \ 10 | if (message) return message; } while (0) 11 | 12 | extern int tests_run; 13 | 14 | 15 | static const char *test_sensor_paths(); 16 | static const char *test_fan_paths(); 17 | static const char *test_get_temp(); 18 | static const char *test_config_file(); 19 | static const char *test_settings(); 20 | static void handler(int signal); 21 | static const char *test_sighup_receive(); 22 | static const char *test_settings_reload(); 23 | static const char *all_tests(); 24 | 25 | int tests(); 26 | 27 | #endif --------------------------------------------------------------------------------